C++提供的默認拷貝構造函數工作的方法是:完成一個成員一個成員的拷貝,如果成員是類對象,則調用
其拷貝構造函數或者默認拷貝構造函數。
在默認拷貝構造函數中,拷貝的策略是逐個成員依次拷貝,但是,一個類可能會擁有資源,如果拷貝構造函數
簡單地製作了一個該資源的拷貝,而不對它本身分配,就得面臨一個麻煩的局面:兩個對象都擁有同一個資源
。當對象析構時,該資源將經歷兩次資源返還。
下面的程序描述了Person對象被簡單拷貝後,面臨析構時的困惑。
using namespace std;
{
public:
Person(char *pN)
{
cout <<"Constructing "<<pN<<endl;
pName=new char (strlen(pN)+1);
if (pName!=0)
{
strcpy(pName,pN);
}
}
~Person()
{
cout<<"Destructing "<<pName<<endl;
pName[0]='\0';
delete pName;
}
protected:
char *pName;
{
Person p1("Randy");
Person p2=p1; //即Person p2 (p1);
result is :
Constructing Randy
Destructing Randy
Destructing
*/
/*---------------------------
程序開始運行時,創建p1對象,p1對象的構造函數從堆中分配空間並賦給數據成員pName,
執行,p2=p1時,因爲沒有定義拷貝構造函數,於是就調用默認拷貝構造函數,使得p2與p1完全一樣,
並沒有新分配堆空間給p2, p1與p2的pName都是同一個值。析構p2時,將堆中字符串清成空串,然後
將堆空間返還給系統; 析構p1時,因爲這是pName指向的是空串,所以第三行輸出中顯示的只是Destructing,
當執行 delete pName ; 按道理系統應該報錯,但在gcc中沒有
創建p2時,對象p1被複制了p2,但資源並未複製,因此,p1和p2指向同一個資源,這稱爲淺拷貝。
當一個對象創建時,分配了資源,這時,就需要定義自己的拷貝構造函數,使之不但拷貝成員,也拷貝資源
using namespace std;
{
public:
Person(char *pN)
{
cout <<"Constructing "<<pN<<endl;
pName=new char (strlen(pN)+1);
if (pName!=0)
{
strcpy(pName,pN);
}
}
Person(Person& p)
{
cout <<"copying "<<p.pName<<"into its own block\n";
pName=new char [sizeof(p.pName)];
if (pName!=0)
strcpy(pName,p.pName);
}
~Person()
{
cout<<"Destructing "<<pName<<endl;
pName[0]='\0';
delete pName;
}
protected:
char *pName;
{
Person p1("Randy");
Person p2=p1; //即Person p2 (p1);
result is :
Constructing Randy
copying Randyinto its own block
Destructing Randy
Destructing Randy
創建p2時,對象p1被複制給了p2,同時資源也作了複製,因此p1和p2指向不同的資源,這稱爲深拷貝。
堆內存並不是唯一需要拷貝構造函數的資源,但它是最常用的一個。打開文件,佔有硬設備(例如打印機
)服務也需要深拷貝。他們也是析構函數必須返還的資源類型。因此一個很好的經驗是:
釋放,此時,對象的拷貝就不是淺拷貝了。
---------------------------------------------*/