複製物件

在一般的情況下,當我們創立一個物件實例時,可以使用另一個物件將之初始化,程式會自動幫我們完成複製的動作,不需要另外撰寫複製物件的成員函式,例如下面程式碼的class2,我們可以看到class2和class1的成員值相同,而兩個物件分別佔有不同的記憶體空間。

class SomeClass{
public:
    int m_x;
    int m_y;
    SomeClass(int a, int b);
};

int main() {
    SomeClass class1(5,6);
    SomeClass class2 = class1;
    int x1 = class1.m_x;      //x1=5
    int y1 = class1.m_y;      //y1=6
    int x2 = class2.m_x;      //x2=5
    int y2 = class2.m_y;      //y2=6
    int addr1 = (int)&class1; //addr1=1638172
    int addr2 = (int)&class2; //addr2=1638156
    return 0;
}

SomeClass::SomeClass(int x, int y){
    m_x = x;
    m_y = y;
}

在某些類別上這樣會有問題,尤其是在成員變數包括指標時,比如我們將SomeClass增加一個成員變數*m_data,以下為更改後的SomeClass類別

class SomeClass{
public:
    int m_x;
    int m_y;
    int *m_data;
    SomeClass(int a, int b, int num);
};
SomeClass::SomeClass(int x, int y, int num){
    m_x = x;
    m_y = y;
    m_data = new int[num];
    for(int i=0; i

表面上看起來沒有問題,但是當class2複製class1的時候,自然包括m_data指標,如果class1資源先被回收了,此時存取class2的m_data資料會有危險,而且假使更改其中一個實例的m_data資料,另一個也會改變。

class SomeClass{
public:
    int m_length;
    int *m_data;
    SomeClass(int num);
};
SomeClass::SomeClass(int num){
    m_length = num;
    m_data = new int[m_length];
    for(int i=0; i

為了避免這樣的錯誤,我們可以定義一個複製函式,當遇到指標成員時,產生一個新的資源並指定位址給該成員,除了撰寫複製建構函式之外,最好再重載等號運算子,因為等號運算子預設也是將物件的屬性值一一複製過去,重載等號運算子,在遇到指標成員時,會產生新的資源,以下為SomeClass類別的改寫。

class SomeClass{
public:
    int m_length;
    int *m_data;
    SomeClass(int num);
    SomeClass(const SomeClass &in);
};

int main() {
    SomeClass class1(2);
    SomeClass class2 = class1;
    class1.m_data[0] = 1;
    int x = class2.m_data[0]; //x=0,不會因為class1更改而隨著更改
    return 0;
}

SomeClass::SomeClass(int num){
    m_length = num;
    m_data = new int[m_length];
    for(int i=0; i