c++類的中有兩個特殊的建構函式,(1)無參建構函式,(2)拷貝建構函式。它們的特殊之處在於:
(1) 當類中沒有定義任何建構函式時,編譯器會預設提供乙個無參建構函式且其函式體為空;
(2) 當類中沒有定義拷貝建構函式時,編譯器會預設提供乙個拷貝建構函式,進行成員變數之間的拷貝。(這個拷貝操作是淺拷貝)
這裡只講拷貝建構函式。在c語言中,
int a = 5; //初始化
int b;
b = 6; //賦值
上面的初始化及賦值操作是最正常不過的語法,c++語言肩挑相容c語言語法的責任,所以在類的設計上,也相容這種操作:
class cls
int main(void)
如上的初始化類需要呼叫到cls類的預設實現的拷貝建構函式,為類賦值需要呼叫的是cls類的預設實現的賦值操作符過載函式,它們都是淺度拷貝的。前者其原型為:
cls(const cls& c)
預設的拷貝建構函式存在弊端,看如下類定義:
class testcls
編譯執行確實不會出錯:
類在我們沒有定義拷貝建構函式的時候,會預設定義預設拷貝建構函式,也就是說可以直接用同型別的類間可以相互賦值、初始化:
int main(void)
編譯通過,執行卻出錯了:
原因就在於,預設的拷貝建構函式實現的是淺拷貝。
深度拷貝和淺拷貝在c語言中就經常遇到的了,在這裡我簡單描述。
一般的賦值操作是深度拷貝:
//深度拷貝
int a = 5;
int b = a;
簡單的指標指向,則是淺拷貝:
//淺拷貝
int a = 8;
int *p;
p = &a;
char* str1 = "helloworld";
char* str2 = str1;
將上面的淺拷貝改為深度拷貝後:
//深度拷貝
int a = 8;
int *p = new int;
*p = a;
char* str1 = "helloworld";
int len = strlen(str1);
char *str2 = new char[len];
memcpy(str2, str1, len);
總而言之,拷貝者和被拷貝者若是同乙個位址,則為淺拷貝,反之為深拷貝。例:以字串拷貝為例,淺拷貝後,str1和str2同指向0x123456,不管哪乙個指標,對該空間內容的修改都會影響另乙個指標。
深拷貝後,str1和str2指向不同的記憶體空間,各自的空間的內容一樣。因為空間不同,所以不管哪乙個指標,對該空間內容的修改都不會影響另乙個指標。
類的預設拷貝建構函式只會用被拷貝類的成員的值為拷貝類簡單初始化,也就是說二者的p指標指向的記憶體空間是一致的。以前面testcls可以知道,編譯器為我們預設定義的拷貝建構函式為:
testcls(const testcls& testcls)
解釋:main函式將要退出時,拷貝類t2的析構函式先得到執行,它把自身p指向的堆空間釋放了;接下來,t1的析構函式得到呼叫,被拷貝類t1的析構函式得到呼叫,它同樣要去析構自身的p指向指向的堆空間,但是該空間和t2類中p指向的空間一樣,造成重複釋放,程式執行崩潰。
解決辦法十分簡單,自定義拷貝建構函式,裡面用深度拷貝的方式為拷貝類初始化:
關於c++拷貝建構函式的深度拷貝和淺拷貝的介紹到這裡,其實還可以將它們的位址列印出來看看,不過這一步就不再贅述了。
自定義拷貝建構函式,並設定為private屬性,其實現體可以什麼都不寫,那麼這個類將變成乙個不可被複製的類了。
[參考博文:
C 有關拷貝建構函式(預設 淺 深拷貝建構函式)
拷貝結構函式顧名思義就是複製物件。先講一下預設拷貝函式 預設拷貝就是直接賦值,讓程式呼叫預設拷貝結構函式。student p1 student p2 p1 或者student p2 p1 程式開始執行時,建立p1物件,p1物件的建構函式從堆中分配空間並賦給資料成員pname,執行,p2 p1時,因為...
c 預設的拷貝建構函式是淺拷貝
c 預設的拷貝建構函式是淺拷貝 淺拷貝就是物件的資料成員之間的簡單賦值,如你設計了乙個沒有類而沒有提供它的複製建構函式,當用該類的乙個物件去給令乙個物件賦值時所執行的過程就是淺拷貝,如 class a a private int data int main 這一句b a 就是淺拷貝,執行完這句後b....
C 拷貝建構函式 淺拷貝和深拷貝
建構函式可以沒有,也可以有多個。複製建構函式只有乙個,不定義編譯器自動生成,使用者寫就使用自定義的複製建構函式 物件之間的複製語句不會呼叫複製建構函式。利用編譯器提供的拷貝建構函式,會做淺拷貝 淺拷貝帶來的問題就死堆區記憶體的重複釋放 案例1 class student student 程式正常輸出...