1. 程式人生 > >C++ Copy Constructor (拷貝建構函式,複製建構函式)

C++ Copy Constructor (拷貝建構函式,複製建構函式)

1.什麼是Copy Constructor?

Copy Constructor 是一個特殊的建構函式,一般只有一個引數,這個引數一般是用const修飾的,對自己類的一個引用(reference)。什麼時候會用到Copy Constructor?

當我們定義一個物件時,它是由另外一個物件來初始化的時候就用到Copy Constructor了。還有就是在一個方法以值作為引數傳進去或者一個方法中以值作為返回。

對於我這新手C++,Copy Constructor 用的比較少,真正用到了也不知道,因為一個類缺少 Copy Constructor 時,編譯器會自動生成一個。

2.趕快來看一個Copy Constructor例子

2.1系統預設會提供一個Copy Constructor

class People{
private:
	int m_age;
public:
	People(int age):m_age(age){
		cout << "constructor" << endl;
	}

	~People(){
		cout << "destructor" << endl;
	}
	int getAge() const{
		return m_age;
	}
};

一個簡單的類,叫People,只有一個屬性age
People p1(18);
    cout << "p1's age " << p1.getAge() << endl;
    People p2(p1);
    cout << "p2's age " << p2.getAge() << endl;

當我們呼叫上面的程式碼時的輸出結果就是:

程式碼工作非常符合我們預期,因為系統會提供一個Copy Constructor,對類裡的屬性進行簡單的賦值工作。


2.2自定義的Copy Constructor

class People{
private:
    int m_age;
public:
    People(int age):m_age(age){
        cout << "constructor" << endl;
    }
    //copy constructor
    People(const People& p){
        cout << "copy constructor" << endl;
    }

    ~People(){
        cout << "destructor" << endl;
    }
    int getAge() const{
        return m_age;
    }
};
People p1(18);
    cout << "p1's age " << p1.getAge() << endl;
    People p2(p1);
    cout << "p2's age " << p2.getAge() << endl;


這次我們自定義了一個Copy Constructor

還是呼叫上次的程式碼,結果:

看,成功的呼叫了我們的copy constructor,因為我們的copy constructor 裡什麼也沒做,所以p2的age就是一個未初始化的int值了。

2.3還有兩種情況會觸發呼叫Copy constructor

//作為一個值從函式中返回
People getPeople(){
	People p1(20);
	return p1;//會呼叫 copy constructor
}
//以值作為引數傳入
void setPeople(People p1){//以值傳入會呼叫 copy constructor
	//do nothing
}

void setPeople(People& p1){//以引用傳入不會呼叫 copy constructor
	//do nothing
}

因為呼叫copy constructor的消耗比較大,所以一般都以引用方式作為函式引數。

3. copy constructor 與 assignment operator 的區別

我們忘記提到的是下面這種寫法也會觸發copy constructor:

People p1(18);
	cout << "p1's age " << p1.getAge() << endl;
	People p2 = p1;
	cout << "p2's age " << p2.getAge() << endl;

這種寫法似乎跟賦值很像。

我們修改People的類,增加過載=操作符。

class People{
private:
	int m_age;
public:
	People(int age):m_age(age){
		cout << "constructor" << endl;
	}

	People(const People& p){
		cout << "copy constructor" << endl;
	}

	People& operator=(const People& p1){
        cout << "assignment operator" << endl;
        m_age = p1.getAge();
        return *this;
    }

	~People(){
		cout << "destructor" << endl;
	}
	int getAge() const{
		return m_age;
	}
};
//測試程式碼
People p1(18);
    cout << "p1's age " << p1.getAge() << endl;
    People p2 = p1;
    cout << "p2's age " << p2.getAge() << endl;
    p2 = p1;
cout << p2.getAge() << endl; 


通過上面的輸出結果我們發現:

= 什麼時候會呼叫Copy Constructor呢?在初始化的時候,也就是第一個 People p2 = p1。因為Copy Constructor 是一種 Constructor,也是負責初始化的。

什麼時候=是賦值呢?兩個都已經初始化,再呼叫=就是賦值了。

4. 淺拷貝和深拷貝

我們上面的類People只有一個簡單的屬性age,還是int型別的,進行簡單的賦值就是淺拷貝,但是People類將來會變的複雜起來,後面可能會增加指標型別的屬性。那麼就會涉及到深拷貝了。

我的理解就是:淺拷貝:兩個變數進行淺拷貝時,它們指向同一個地址,它們的值相同。這樣會有問題,當其中的一個析構了那個地址,另外一個也沒有了,有時候會發生錯誤,但淺拷貝比較廉價。

深拷貝:兩個變數進行深拷貝時,第二變數會重新申請一塊區域來存放跟第一個變數指向地址的值。兩個東西完全是獨立的,只是值相同。消耗比較大,因為要重新申請空間。

關於Copy Constructor,就先這樣吧。

5.禁用Copy Constructor

把Copy Constructor設定為private就行了。以我的水平,我還不知道什麼情況要這樣做 =。=