13.1.1 合成的復制構造函數
與合成的默認構造函數不同,即使我們定義了其他構造函數,也可合成復制構造函數。合成復制構造函數(synthesized copy constructor)的行為是,執行逐個成員初始化(memberwise initialize),將新對象初始化為原對象的副本。
所謂“逐個成員”,指的是編譯器將現有對象的每個非static成員,依次復制到正創建的對象。只有一個例外,每個成員的類型決定了復制該成員的含義。合成復制構造函數直接復制內置類型成員的值,類類型成員使用該類的復制構造函數進行復制。數組成員的復制是個例外。雖然一般不能復制數組,但如果一個類具有數組成員,則合成復制構造函數將復制數組。復制數組時合成復制構造函數將復制數組的每一個元素。
13.1.2 定義自己的復制構造函數
復制構造函數就是接受單個類類型引用形參(通常用const修飾)的構造函數。
雖然也可以定義接受非const引用的復制構造函數,但形參通常是一個const引用。因為用於向函數傳遞對象和從函數返回對象,該構造函數一般不應設置為explicit。復制構造函數應將實參的成員復制到正在構造的對象。
只包含類類型成員或內置類型(但不是指針類型)成員的類,無須顯式地定義復制構造函數,也可以復制。
有些類必須對復制對象時發生的事情加以控制。這樣的類經常有一個數據成員是指針,或者有成員表示在構造函數中分配的其他資源。而另一些類在創建新對象時必須做一些特定工作。這兩種情況下,都必須定義復制構造函數。
定義復制構造函數最困難的部分在於認識到需要復制構造函數。復制構造函數的定義與其他構造函數一樣:它與類同名,沒有返回值,可以(而且應該)使用構造函數初始化列表初始化新創建對象的成員,可以在函數體中做任何其他必要工作。
class Class5
{
public:
int *k;
Class5(int i)
{
k = &i;
}
Class5(const Class5 &c5)
{
int i = *(c5.k);
k = &i;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
int i = 5;
int *j = &i;
cout << j << endl;
//0012FF60
int *k = j;
cout << k << endl;
//0012FF60
int m = *k;
cout << &m << endl;
//0012FF3C
Class5 c5(10);
cout << c5.k << endl;
//0012FE50
Class5 c4 = c5;
cout << c4.k << endl;
//0012FE34
return 0;
}
13.1.3 禁止復制
有些類需要完全禁止復制。例如,iostream類就不允許復制。
為了防止復制,類必須顯式聲明其復制構造函數為private。
如果復制構造函數是私有的,將不允許用戶代碼復制到該類類型的對象,編譯器將拒絕任何進行復制的嘗試。
然而,類的友元和成員仍可以進行復制。如果想要連友元和成員中的復制也禁止,就可以聲明一個(private)復制構造函數但不對其定義。
聲明而不定義成員函數是合法的,但是,使用未定義成員的任何嘗試將導致鏈接失敗。通過聲明(但不定義)private復制構造函數,可以禁止任何復制類類型對象的嘗試:用戶代碼中的復制嘗試將在編譯時標記為錯誤,而成員函數和友元中的復制嘗試將在鏈接時導致錯誤。
大多數類應定義復制構造函數和默認構造函數。
不允許復制的類對象只能作為引用傳遞給函數或從函數返回,它們也不能用作容器的元素。
一般來說,最好顯式或隱式定義默認構造函數和復制構造函數。只有不存在其他構造函數時才合成默認構造函數。如果定義了復制構造函數,也必須定義默認構造函數。
摘自 xufei96的專欄