先考慮一種情況,對一個已知對象進行拷貝,編譯系統(tǒng)會自動調(diào)用一種構(gòu)造函數(shù)——拷貝構(gòu)造函數(shù),如果用戶未定義拷貝構(gòu)造函數(shù),則會調(diào)用默認拷貝構(gòu)造函數(shù)。
先看一個例子,有一個學生類,數(shù)據(jù)成員時學生的人數(shù)和名字:
cout << 'Student' << endl; cout << '~Student ' << (int)name << endl;
 執(zhí)行結(jié)果:調(diào)用一次構(gòu)造函數(shù),調(diào)用兩次析構(gòu)函數(shù),兩個對象的指針成員所指內(nèi)存相同,這會導致什么問題呢?name指針被分配一次內(nèi)存,但是程序結(jié)束時該內(nèi)存卻被釋放了兩次,會導致崩潰!

這是由于編譯系統(tǒng)在我們沒有自己定義拷貝構(gòu)造函數(shù)時,會在拷貝對象時調(diào)用默認拷貝構(gòu)造函數(shù),進行的是淺拷貝!即對指針name拷貝后會出現(xiàn)兩個指針指向同一個內(nèi)存空間。

所以,在對含有指針成員的對象進行拷貝時,必須要自己定義拷貝構(gòu)造函數(shù),使拷貝后的對象指針成員有自己的內(nèi)存空間,即進行深拷貝,這樣就避免了內(nèi)存泄漏發(fā)生。
添加了自己定義拷貝構(gòu)造函數(shù)的例子:
Student(const Student &s);//拷貝構(gòu)造函數(shù),const防止對象被改變 cout << 'Student' << endl; cout << '~Student ' << (int)name << endl; Student::Student(const Student &s) memcpy(name, s.name, strlen(s.name)); cout << 'copy Student' << endl;
 執(zhí)行結(jié)果:調(diào)用一次構(gòu)造函數(shù),一次自定義拷貝構(gòu)造函數(shù),兩次析構(gòu)函數(shù)。兩個對象的指針成員所指內(nèi)存不同。 總結(jié):淺拷貝只是對指針的拷貝,拷貝后兩個指針指向同一個內(nèi)存空間,深拷貝不但對指針進行拷貝,而且對指針指向的內(nèi)容進行拷貝,經(jīng)深拷貝后的指針是指向兩個不同地址的指針。 再說幾句: 當對象中存在指針成員時,除了在復制對象時需要考慮自定義拷貝構(gòu)造函數(shù),還應(yīng)該考慮以下兩種情形: 1.當函數(shù)的參數(shù)為對象時,實參傳遞給形參的實際上是實參的一個拷貝對象,系統(tǒng)自動通過拷貝構(gòu)造函數(shù)實現(xiàn); 2.當函數(shù)的返回值為一個對象時,該對象實際上是函數(shù)內(nèi)對象的一個拷貝,用于返回函數(shù)調(diào)用處。
3.淺拷貝帶來問題的本質(zhì)在于析構(gòu)函數(shù)釋放多次堆內(nèi)存,使用std::shared_ptr,可以完美解決這個問題。
關(guān)于std::shared_ptr的原理和實現(xiàn)可參考:C++筆試題之smart pointer的實現(xiàn)
一個完整的自定義類實現(xiàn)可參考:C++筆試題之String類的實現(xiàn)
參考鏈接:https://www.cnblogs.com/always-chang/p/6107437.html
|