網頁

2022/8/17

C++ 複製建構式/複製建構子 copy constructor

C++的複製建構式/拷貝建構式(copy constructor)用來複製一個物件來建構另一新物件。


建構式傳入一個型別同自身的參照參數則為複製建構式。

例如下面類別Car的建構式Car(const Car& car)為複製建構式,將傳入的同類別物件參數屬性值分派到對應的建構物件屬性。

main.cpp

#include <iostream>
#include <string>
using namespace std;

class Car {
    private:
        // 屬性 attributes
        string model;
        int speed;
        int* rank;

    public:
        // 建構式 constructor
        Car() { rank = new int; }

        // 複製建構式
        Car(const Car& car) : model(car.model), speed(car.speed), rank(new int(*car.rank)) {}

        // 解構式 destructor
        ~Car() {
            cout << "destruct " << model << " rank:" << *rank << endl;
            delete rank;
        }

        // 方法 methods
        string GetModel() { return model; }
        void SetModel(string model) { this->model = model; }
        int GetSpeed() { return speed; }
        void SetSpeed(int speed) { this->speed = speed; }
        int GetRank() { return *rank; }
        void SetRank(int rank) { *(this->rank) = rank; }
};

int main() {
    Car car1;
    car1.SetModel("Tesla Model S");
    car1.SetRank(1);

    Car car2 = Car(car1); // 呼叫複製建構式
    car2.SetRank(2);

    cout << car1.GetRank() << endl; // 1
    cout << car2.GetRank() << endl; // 2

    return 0;
}

執行印出以下,在離開main()函式時物件的解構式會自動被調用。

1
2
destruct Tesla Model S rank:2
destruct Tesla Model S rank:1

注意成員屬性rank為指向new產生的動態記憶體的指標,因此在複製建構式中必須同樣以new的方式將傳入參數的同屬性指標所指的值複製並產生另一動態記憶體空間。若直接將指標傳入rank初始化建構式則car1car2rank指向的是同一個動態記憶體空間,可能造成程式執行的bug且在解構式delete釋放物件的同個動態記憶體空間時發生錯誤。

例如修改複製建構式如下:

main.cpp

#include <iostream>
#include <string>
using namespace std;

class Car {
    private:
        // 屬性 attributes
        string model;
        int speed;
        int* rank;

    public:
        // 建構式 constructor
        Car() { rank = new int; }

        // 複製建構式
        Car(const Car& car) : model(car.model), speed(car.speed), rank(car.rank) {}

        // 解構式 destructor
        ~Car() {
            cout << "destruct " << model << " rank:" << *rank << endl;
            delete rank;
        }

        // 方法 methods
        string GetModel() { return model; }
        void SetModel(string model) { this->model = model; }
        int GetSpeed() { return speed; }
        void SetSpeed(int speed) { this->speed = speed; }
        int GetRank() { return *rank; }
        void SetRank(int rank) { *(this->rank) = rank; }
};

int main() {
    Car car1;
    car1.SetModel("Tesla Model S");
    car1.SetRank(1);

    Car car2 = Car(car1); // 呼叫複製建構式
    car2.SetRank(2);

    cout << car1.GetRank() << endl; // 2
    cout << car2.GetRank() << endl; // 2

    return 0;
}

則執行印出以下。car1car2rank為同一因此修改互相影響,且兩物件的解構時重複釋放同個rank的動態記憶體而出現錯誤。

2
2
destruct Tesla Model S rank:2
destruct Tesla Model S rank:2
main(94331,0x1114aee00) malloc: *** error for object 0x7ffb6fc05a40: pointer being freed was not allocated
main(94331,0x1114aee00) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6

若類別未定義複製建構式則編譯器會隱含地自動產生一個預設的複製建構式,其在複製屬性值時為直接將值傳入屬性的初始化建構式同上,也就是說若類別成員屬性有動態記憶體的指標必須自行實作複製建構式以避免如上的問題。


沒有留言:

張貼留言