類構造函數和動態內存分配


1. c++自動提供了以下的成員函數

默認構造函數,如果沒有定義構造函數

默認析構函數,如果沒有定義

復制構造函數,如果沒有定義,java參見:https://blog.csdn.net/ShewMi/article/details/80788591

賦值運算符,如果沒有定義

地址運算符,如果沒有定義

移動構造函數

移動賦值運算符

a. 默認構造函數

編譯器在沒有提供構造函數時候,編譯器將提供一個不接受任何參數,也不執行任何操作的構造函數

如果定義了構造函數,編譯器將不提供任何默認構造函數,如果希望創建對象時不顯示的對它進行初始化,則必須顯示定義默認構造函數,這種構造函數沒有任何參數,但是可以使用它來設置特定的值

b. 復制構造函數

復制構造函數用於將一個對象復制到一個新創建的對象中,也就是說,它用於初始化過程中,而不是常規的賦值過程中

調用的時機:

當函數的參數為類對象的時候

函數的返回值是類對象

對象需要另一個對象進行初始化

由於按值傳遞對象將調用賦值構造函數,因此應該使用按引用傳遞對象,這樣可以節省調用構造函數的時間以及存儲新對象的空間。

默認賦值構造函數的功能:逐個賦值非靜態成員(成員賦值也稱淺賦值),賦值的是成員的值,如果類中存在一個靜態的對象計數變量,默認的復制構造函數不說明其行為,也不增加計數器的個數,但是析構函數會更新次數,解決方法是提供一個對計數進行更新的顯示復制構造函數

 如果對象中存在指針變量,默認的復制構造函數將不會為該指針分配空間,復制時候會將原對象和復制對象的指針指向同一個位置,析構兩個對象會對同一個指針delete兩次,從而會出現嚴重的問題。

警告:如果類中包含了使用new 初始化的指針成員,應當定義一個復制構造函數,以復制指向的數據,而不是指針,這稱為深度復制。復制的另一種形式(成員復制或者淺復制)只是復制指針值,淺復制僅淺淺復制指針的信息,而不會深入挖掘以復制指針引用的結構

賦值構造函數本質是構造函數,所以可以按照下面方式顯式調用:

c. 賦值運算符:

String knot;

String metoo = knot;  // 調用的拷貝構造函數

String youtoo;

youtoo = knot;  // 調用賦值運算符重載

默認的賦值運算符隱式實現也是對成員進行逐個賦值,如果成員本身就是類對象,則程序將使用這個類定義的賦值運算符來復制這個成員,靜態數據成員不受影響。

注意:由於目標對象可能以及引用了以前分配的數據,所以函數應該使用delete[] 來釋放這些數據

函數應當避免將對象賦值給自身,否則,在給對象重新賦值之前,釋放內存操作可能會刪除對象的內容。

函數返回一個指向調用對象的引用,通過返回一個對象,函數可以像賦值常規操作那樣,連續進行賦值

d. 默認析構函數:

如果類中有使用new的指針成員變量,析構函數是必不可少的,此外,析構函數一般被定義為virtual,這是為了重載以后被重載的類也能夠正常的釋放內存

例:String類

#include <iostream>
using namespace std;
class String
{
private:
    char* str;
    int len;
    static int num_strings;
    static const int CINLIM = 80;    // static 修飾的常量在類中初始化
public:
    // constructors and other methods
    String();
    String(const char*);
    String(const String&);
    ~String();
    int length() const {
        return len; 
    }
    // overloaded operator methods
    String& operator=(const String &);
    String& operator=(const char *);
    char& operator[](int);
    const char& operator[](int) const;
    // overloaded operator friends
    friend bool operator<(const String&, const String&);
    friend bool operator>(const String&, const String&);
    friend bool operator==(const String&, const String&);
    friend ostream& operator<<(ostream&, const String&);
    friend istream& operator>>(istream&, String&);
    // static function
    static int howMany();
};
#include "String.h"
#include <cstring>
#include <iostream>
using namespace std;
#pragma warning(disable:4996)

int String::num_strings = 0;
int String::howMany() {
    return num_strings;
}

String::String(const char* s) {
    len = strlen(s);
    str = new char[len + 1];
    strcpy(str, s);
    num_strings++;
}

String::String() {
    len = 0;
    str = new char[1];
    str[0] = '\0';
    num_strings++;
}

String::String(const String& st) {
    num_strings++;
    len = st.len;
    str = new char[len + 1];
    strcpy(str, st.str);
}

String::~String() {
    --num_strings;
    delete[] str;
}

String& String::operator=(const String& st) {
    if (this == &st) {
        return *this;
    }
    delete[] str;
    len = st.len;
    str = new char[len + 1];
    strcpy(str, st.str);
    return *this;
}

String& String::operator=(const char* s) {
    delete[] str;
    len = strlen(s);
    str = new char[len + 1];
    strcpy(str, s);
    return *this;
}

char& String::operator[](int i) {
    return str[i];
}

const char& String::operator[](int i) const {
    return str[i];
}

bool operator<(const String& str1, const String& str2) {
    return strcmp(str1.str, str2.str);
}

bool operator>(const String& str1, const String& str2) {
    return str2 < str1;
}

bool operator==(const String& str1, const String& str2) {
    return strcmp(str1.str, str2.str) == 0;
}

ostream& operator<<(ostream& os, const String& st) {
    os << st.str;
    return os;
}

istream& operator>>(istream& is, String& st) {
    char temp[String::CINLIM];
    is.get(temp, String::CINLIM);
    if (is) st = temp;
    while (is && is.get() != '\n') {
        continue;
    }
    return is;
}

測試代碼:

 if(!cin || temp[0] == '0')

  break;

較早的get(char*, int)在 讀取到空行后,返回的值不為false,然而,對於這些版本來說,如果讀取了一個空行,則字符串的第一個字符將會是一個空字符

if(!cin ||  temp[0] == '\0')

  break;

如果執行了最新的c++標准,則if語句中的第一個條件將檢測到空行,第二個條件用於舊版本實現檢測空行。

2. 在構造函數中使用new時候應當注意的事項

a. 如果在構造函數中使用new來初始化指針成員,則應當在析構函數中使用delete

b. new 和delete必須互相兼容,new對應delete,new[] 對應於delete[]

c. 如果有多個構造函數,則必須以相同的方式使用New,要么都帶括號,要么都不帶,因為只有一個析構函數,所有的構造函數必須與它兼容,然而可以在一個構造函數中使用new初始化指針,另一個構造函數中將指針初始化為空,這是因為delete(無論是帶中括號還是不帶中括號)可以用於空指針。

NULL,0,還是nullptr:以前空指針可以用於0或NULL來表示,C程序員通常使用NULL而不是0,以指出這是一個指針,就像使用'\0'而不是0來表示空字符,以指出這是一個字符一樣,然而C++傳統上更喜歡使用簡單的0,而不是等價的NULL,但C++11提供了關鍵字nullptr,這將是一種更好的選擇。

d. 應當定義一個復制構造函數,通過深度復制將一個對象初始化為另一個對象。

e. 應當定義一個賦值運算符,通過深度復制將一個對象復制給另一個對象。

3. 返回對象的說明

a. 返回指向const對象的引用

使用const引用的常見原因是旨在提高效率,對於何時可以采用這種方法存在一些限制,如果函數返回傳遞給它的對象,可以通過返回引用來提高效率。

需要說明:返回對象調用復制構造函數,而返回引用不用。引用指向的對象應該在調用函數執行時存在,傳遞的對象是const,這樣返回的類型必須是const,這樣才匹配。

b. 返回指向非const對象的引用

兩種常見的返回非const對象情形:重載復制運算符以及重載與cout一起使用的<<運算符,operator=返回值用於連續賦值,operator<<用於拼接輸出

c. 返回對象:

如果返回的對象是被調用函數中的局部變量。

d. 返回const對象

 4. 使用指向對象的指針

 5. 隊列模擬:

#include <cstdlib>
class Customer {
private:
    long arrive;
    int processtime;
public:
    Customer() {
        arrive = processtime = 0;
    }

    void set(long when) {
        processtime = rand() % 3 + 1;
        arrive = when;
    }

    long when() const{
        return arrive;
    }

    int ptime() const {
        return processtime;
    }
};
#include "Customer.h"
typedef Customer Item;
class Queue {
private:
    struct Node {
        Item item;
        Node* next;
    };
    enum {
        Q_SIZE = 10
    };
    Node* front;
    Node* rear;
    int items;    // current number of items in Queue
    const int qsize;    // maximum number of items in Queue
    // 防止系統產生默認的拷貝構造函數
    // 聲明為私有后,調用Queue snick = nup; 以及 ss = nup系統將報錯
    Queue(const Queue& q) : qsize(10) {}    // const修飾的非靜態常量,必須使用初始化列表的方式初始化,同時也必須采用這種格式初始化引用數據成員
    Queue& operator=(const Queue& q) { return *this; }
public:
    Queue(int qs = Q_SIZE);
    ~Queue();
    bool isEmpty() const;
    bool isFull() const;
    int queueCount() const;
    bool enqueue(const Item& item);    // add item to end
    bool dequeue(Item& item);    // remove item from front
};
#include "Queue.h"

Queue::Queue(int qs) : qsize(qs) {    
    front = nullptr;
    rear = nullptr;
    items = 0;
}

Queue::~Queue() {
    Node* temp;
    while (front != nullptr) {
        temp = front;
        front = front->next;
        delete temp;
    }
}

int Queue::queueCount() const{
    return items;
}

bool Queue::isEmpty() const {
    return items == 0;
}

bool Queue::isFull() const {
    return items == qsize;
}

bool Queue::enqueue(const Item& item) {
    if (isFull()) {
        return false;
    }
    Node* add = new Node;
    add->item = item;
    add->next = NULL;
    items++;
    if (front == nullptr) {
        front = add;
    }
    else {
        rear->next = add;
    }
    rear = add;
    return true;
}

bool Queue::dequeue(Item& item) {
    if (front == nullptr) {
        return false;
    }
    item = front->item;
    items--;
    Node* temp = front;
    front = front->next;
    delete temp;
    if (items == 0) {
        rear = nullptr;
    }
    return true;
}
#include "Queue.h"
const int MIN_PER_HR = 60;
bool newcustomer(double x) {
    return (rand() * x / RAND_MAX) < 1;        // rand(),RADN_MAX在cstdlib文件中
}
int main() {
    srand(time(0));    // random initializing of rand()
    cout << "Case Study: Bank of Heather Automatic Teller\n";
    cout << "Enter maxinm size of queue:";
    int qs;
    cin >> qs;
    Queue line(qs);
    cout << "Enter the number of simulation hours: ";
    int hours;
    cin >> hours;
    long cyclelimit = MIN_PER_HR * hours;
    cout << "Enter the average of number of customer per hour: ";
    double perhour;
    cin >> perhour;
    double min_per_cust;
    min_per_cust = MIN_PER_HR;
    Item temp;
    long turnaways = 0;
    long customers = 0;
    long served = 0;
    long sum_line = 0;
    int wait_time = 0;
    long line_wait = 0;
    for (int cycle = 0; cycle < cyclelimit; cycle++) {
        if (newcustomer(min_per_cust)) {
            if (line.isFull()) {
                turnaways++;
            }
            else {
                customers++;
                temp.set(cycle);
                line.enqueue(temp);
            }
        }
        if (wait_time <= 0 && !line.isEmpty()) {
            line.dequeue(temp);
            wait_time = temp.ptime();
            line_wait += cycle - temp.when();
            served++;
        }
        if (wait_time > 0) {
            wait_time--;
        }
        sum_line += line.queueCount();
    }
    if (customers > 0) {
        cout << "Customers accepted: " << customers << endl;
        cout << " custormer served: " << served << endl;
        cout << " turnaways: " << turnaways << endl;
        cout << "average queue size: ";
        cout.precision(2);
        cout.setf(ios_base::fixed, ios_base::floatfield);
        cout << (double) sum_line / cyclelimit << endl;
        cout << " average wait time: " << (double)line_wait / served << " minutes\n";
    }
    else {
        cout << "No customers!\n";
    }
    cout << "Done!\n";
    return 0;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM