在C++中經常會遇到有關類對象的淺復制與深復制的問題,也是容易出錯的地方。
查找了相關資料,有關淺復制與深復制的定義為:對類進行復制的時候按位復制,即把一個對象各數據成員的值原樣復制到目標對象中。當類中涉及到指針類型數據成員的時候,往往就會產生指針懸掛問題。
class A{ public: int* a; }; A a1; A b1=a1;
b1=a1執行的是淺復制,此時a1.a和b1.a指向的是同一個內存地址,如果在析構函數里面有對內存的釋放。就會出現內存訪問異常。因為一塊內存空間會被釋放兩次!
參考代碼理解:
#include <iostream> #include <string.h> using namespace std; class person{ private: char *name; int age; public: person(const char *Name,int Age) { name=new char[strlen(Name)+1]; strcpy(name,Name); age=Age; cout<<"construct ..."<<endl; } ~person() { cout<<"destruct ..."<<age<<endl; delete name; } void dispaly() { cout<<name<<" "<<age<<endl; } void setage(int x) { age=x; } }; int main() { person p1("jack",23); person p2=p1; p1.setage(10); // p2.setage(20); p1.dispaly(); p2.dispaly(); return 0; }
從運行結果我們可以看到程序只是調用了一次構造函數,但是卻執行了兩次析構函數,不符合預期期望。對象p2=p1執行的是淺復制,p2中指針name和p1中指針name是指向的同一地址,由於沒有定義構造函數,在執行p2=p1的時候,系統采用默認拷貝構造函數(默認的拷貝構造函數不會為新對象重新分配新的內存空間),即按位的拷貝方式將p1中各數據成員的值拷貝到p2的對應成員中,所以導致p1.name=p2.name(指向了同一內存),此時類person的構造函數只會執行一次。
當遇到main()函數最后的“}”的時候,執行析構函數,由析構函數執行的規律可知先構造的后執行,所以先執行p2的析構函數,此時系統將p2.name指向的存儲單元釋放,在執行p1析構函數的時候,p1.name所指向的內存單元又被釋放,這樣就會造成同一塊內存空間被釋放兩次,造成錯誤,p1.name也就成了懸掛指針。
解決這一問題就需要對p1進行深拷貝,即構造拷貝函數,讓對象p2在拷貝p1的時候獲取新的內存空間。
#include <iostream> #include <string.h> using namespace std; class person{ private: char *name; int age; public: person(const char *Name,int Age)//構造函數 { name=new char[strlen(Name)+1]; strcpy(name,Name); age=Age; cout<<"construct ..."<<endl; } person(const person &p)//拷貝構造函數 { name=new char[strlen(p.name)+1]; strcpy(name,p.name); age=p.age; cout<<"copy construct ..."<<endl; } ~person() { cout<<"destruct ..."<<age<<endl; delete name; } void dispaly() { cout<<name<<" "<<age<<endl; } void setage(int x) { age=x; } }; int main() { person p1("jack",23); person p2=p1; p1.setage(10); // p2.setage(20); p1.dispaly(); p2.dispaly(); return 0; }
從運行結果可以看到符合預期期望,從拷貝構造函數定義可以知道,對類對象的復制,重新為新對象分配了新的內存單元。深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源,當這個類的對象發生復制過程的時候,資源重新分配,這個過程就是深拷貝,反之,沒有重新分配資源,就是淺拷貝。
定義拷貝構造函數的形式為:X(X& x){ a=new ••••••}
什么時候用到拷貝函數?
b.一個對象以值傳遞的方式從函數返回;