淺拷貝與深拷貝
在上一節講解的拷貝建構函數的例子Circle類別中,拷貝的策略都是與系統預設的策略一致,即把原有物件中成員依次拷貝給新物件中對應的成員,既然如此,我們為何還要自己定義呢?原因在於,簡單的將所有情況都按照這種簡單的方式初始化,難免有不同的情況,出現問題。
例如,在剛才的Circle類別中,如果成員變數中加一個指標成員,初始化中需要動態開闢內存,則會出現極大的安全隱患,程式碼如下:
/******************************************//Des:C++教程配套程式//Author :Huang//CopyRight:www.dotcpp.com//Date:2017/8/26****************************** ********/#include<iostream>#include<Cstring>usingnamespacestd;#definePI3.1415classCircle{private:doubleR;char*str;public:Circle(doubleR,char*str);~Circle(); doublearea();doublegirth();};Circle::~Circle(){delete[]str;}Circle::Circle(doubleR,char*str){cout<<Constructor<<endl;this->R=R ;this->str=newchar[strlen(str)+1];strcpy(this->str,str);cout<<this->R<<<<this->str<<endl;}doubleCircle::area (){returnPI*R*R;}doubleCircle::girth(){return2*PI*R;}intmain(){CircleA(5,NO.1Oldclass);CircleB(A);return0;}
為了驗證,在Circle類別中,我們增加了一個指標成員,並且在建構函式中對其初始化,同時沒有自訂拷貝建構子。那麼在主函數中Circle B(A);的這句話將A對象賦值給B對象,將呼叫預設產生的拷貝建構函數,運行後,程式如下圖報錯:
而實際上的原因在於,預設的拷貝建構函數只是進行資料賦值,並不能為指標開闢記憶體空間,相當於程式碼:
This->str=str;
那麼本質上,也就是兩個指標指向一塊堆空間。已經違背了我們的初衷。那麼在程式結束的時候,兩個物件回收的時候,會呼叫自己的析構函數,釋放這塊記憶體空間,由於兩個物件要呼叫兩次,也就是delete兩次,就會出現錯誤!
所以,當類別中有指標類型時,依靠預設的拷貝建構函數的方法,已經無法滿足我們的需求,必須定義一個特定的拷貝建構函數,即不僅可以進行資料的拷貝,也可以為成員分配記憶體空間,實現真正的拷貝,也叫做深拷貝,這就是深拷貝建構函數。
深拷貝建構函數實作:
#include<iostream>#include<Cstring>usingnamespacestd;#definePI3.1415classCircle{private:doubleR;char*str;public:Circle(doubleR,char*str);Circle(Circle&A);~Circle(doubleR,char*str);Circle(Circle&A);~Circle();doublearea(); doublegirth();};Circle::~Circle(){delete[]str;cout<<CallDestructor<<endl;}Circle::Circle(Circle&A){cout<<CopyConstructor<<endl;this->R=AR ;this->str=newchar[strlen(A.str)+1];strcpy(this->str,A.str);}Circle::Circle(doubleR,char*str){cout<<Constructor<<endl ;this->R=R;this->str=newchar[strlen(str)+1];strcpy(this->str,str);}doubleCircle::area(){returnPI*R*R;}doubleCircle: :girth(){return2*PI*R;}intmain(){CircleA(5,NO.1Oldclass);CircleB(A);return0;}
其實現原理與帶參數的建構函數類似,在賦值之前開闢足夠的記憶體空間,來真正完成完整的拷貝,這就是所謂的「深拷貝」。請大家理解後上機實驗!