拷貝構造函數的參數類型必須是引用,而且通常情況下還是const的,但是const并不是嚴格必須的。
#include <iostream>
#include <string>
using namespace std;
class CClass
{
public:
CClass() : a(1), b("Hello, world.")
{
}
// 拷貝構造函數,參數中的const不是嚴格必須的,但引用符号是必須的
CClass(const CClass& c_class)
{
a = c_class.a;
b = c_class.b;
}
void setValues(int a, string b)
{
this->a = a;
this->b = b;
}
void printValues()
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
}
private:
int a;
string b;
};
int main(void)
{
CClass c;
c.setValues(100, "Hello, boys!");
CClass d(c); // 此處調用拷貝構造函數
d.printValues();
return 0;
}
如果将拷貝構造函數中的引用符号去掉&,編譯将無法通過,出錯的資訊如下:
…
非法的複制構造函數: 第一個參數不應是“CClass”
…
沒有可用的複制構造函數或複制構造函數聲明為“explicit”
…
原因:
如果拷貝構造函數中的參數不是一個引用,即形如CClass(const CClass c_class),那麼就相當于采用了傳值的方式(pass-by-value),而傳值的方式會調用該類的拷貝構造函數,進而造成無窮遞歸地調用拷貝構造函數。是以拷貝構造函數的參數必須是一個引用。
需要澄清的是,傳指針其實也是傳值,如果上面的拷貝構造函數寫成CClass(const CClass* c_class),也是不行的。事實上,隻有傳引用不是傳值外,其他所有的傳遞方式都是傳值。
附帶說明,在下面幾種情況下會調用拷貝構造函數:
a. 顯式或隐式地用同類型的一個對象來初始化另外一個對象。如上例中,用對象c初始化d;
b. 作為實參(argument)傳遞給一個函數。如CClass(const CClass c_class)中,就會調用CClass的拷貝構造函數;
c. 在函數體内傳回一個對象時,也會調用傳回值類型的拷貝構造函數;
d. 初始化序列容器中的元素時。比如 vector<string> svec(5),string的預設構造函數和拷貝構造函數都會被調用;
e. 用清單的方式初始化數組元素時。string a[] = {string(“hello”), string(“world”)}; 會調用string的拷貝構造函數。
如果在沒有顯式聲明構造函數的情況下,編譯器都會為一個類合成一個預設的構造函數。如果在一個類中聲明了一個構造函數,那麼就會阻止編譯器為該類合成預設的構造函數。和構造函數不同的是,即便定義了其他構造函數(但沒有定義拷貝構造函數),編譯器總是會為我們合成一個拷貝構造函數。
如果想阻止拷貝構造函數發生作用,那麼一個類,必須顯式聲明其拷貝構造函數,并且将其設為private, 并且其實作體是空的。因為僅僅是private的話,友元函數或者友元類還是有機會調用到這個拷貝構造函數。
通常情況下,如果一個類實作了拷貝構造函數,那麼這個類也需要實作預設構造函數。