| 導購 | 订阅 | 在线投稿
分享
 
 
 

C++類對象的複制-拷貝構造函數

來源:互聯網  2008-06-01 02:03:36  評論

在學習這一章內容前我們已經學習過了類的構造函數和析構函數的相關知識,對于普通類型的對象來說,他們之間的複制是很簡單的,例如: int a = 10;

int b =a;

自己定義的類的對象同樣是對象,誰也不能阻止我們用以下的方式進行複制,例如:#include <iostream>

using namespace std;

class Test

{

public:

Test(int temp)

{

p1=temp;

}

PRotected:

int p1;

};

void main()

{

Test a(99);

Test b=a;

}普通對象和類對象同爲對象,他們之間的特性有相似之處也有不同之處,類對象內部存在成員變量,而普通對象是沒有的,當同樣的複制方法發生在不同的對象上的時候,那麽系統對他們進行的操作也是不一樣的,就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個複制過程的,在上面的代碼中,我們並沒有看到拷貝構造函數,同樣完成了複制工作,這又是爲什麽呢?因爲當一個類沒有自定義的拷貝構造函數的時候系統會自動提供一個默認的拷貝構造函數,來完成複制工作。

下面,我們爲了說明情況,就普通情況而言(以上面的代碼爲例),我們來自己定義一個與系統默認拷貝構造函數一樣的拷貝構造函數,看看它的內部是如何工作的!

代碼如下:#include <iostream>

using namespace std;

class Test

{

public:

Test(int temp)

{

p1=temp;

}

Test(Test &c_t)//這裏就是自定義的拷貝構造函數

{

cout<<"進入copy構造函數"<<endl;

p1=c_t.p1;//這句假如去掉就不能完成複制工作了,此句複制過程的核心語句

}

public:

int p1;

};

void main()

{

Test a(99);

Test b=a;

cout<<b.p1;

cin.get();

}上面代碼中的Test(Test &c_t)就是我們自定義的拷貝構造函數,拷貝構造函數的名稱必須與類名稱一致,函數的形式參數是本類型的一個引用變量,且必須是引用。

當用一個已經初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用,假如你沒有自定義拷貝構造函數的時候系統將會提供給一個默認的拷貝構造函數來完成這個過程,上面代碼的複制核心語句就是通過Test(Test &c_t)拷貝構造函數內的p1=c_t.p1;語句完成的。假如取掉這句代碼,那麽b對象的p1屬性將得到一個未知的隨機值;


C++類對象的複制-拷貝構造函數
更多內容請看C/C++技術專題專題,或

下面我們來討論一下關于淺拷貝和深拷貝的問題。

就上面的代碼情況而言,很多人會問到,既然系統會自動提供一個默認的拷貝構造函數來處理複制,那麽我們沒有意義要去自定義拷貝構造函數呀,對,就普通情況而言這的確是沒有必要的,但在某寫狀況下,類體內的成員是需要開辟動態開辟堆內存的,假如我們不自定義拷貝構造函數而讓系統自己處理,那麽就會導致堆內存的所屬權産生混亂,試想一下,已經開辟的一端堆地址原來是屬于對象a的,由于複制過程發生,b對象取得是a已經開辟的堆地址,一旦程序産生析構,釋放堆的時候,計算機是不可能清楚這段地址是真正屬于誰的,當連續發生兩次析構的時候就出現了運行錯誤。

爲了更具體的說明問題,請看如下的代碼。 #include <iostream>

using namespace std;

class Internet

{

public:

Internet(char *name,char *address)

{

cout<<"載入構造函數"<<endl;

strcpy(Internet::name,name);

strcpy(Internet::address,address);

cname=new char[strlen(name)+1];

if(cname!=NULL)

{

strcpy(Internet::cname,name);

}

}

Internet(Internet &temp)

{

cout<<"載入COPY構造函數"<<endl;

strcpy(Internet::name,temp.name);

strcpy(Internet::address,temp.address);

cname=new char[strlen(name)+1];//這裏注重,深拷貝的體現!

if(cname!=NULL)

{

strcpy(Internet::cname,name);

}

}

~Internet()

{

cout<<"載入析構函數!";

delete[] cname;

cin.get();

}

void show();

protected:

char name[20];

char address[30];

char *cname;

};

void Internet::show()

{

cout<<name<<":"<<address<<cname<<endl;

}

void test(Internet ts)

{

cout<<"載入test函數"<<endl;

}

void main()

{

Internet a("中國軟件開發實驗室","www.cndev-lab.com");

Internet b = a;

b.show();

test(b);

}上面代碼就演示了深拷貝的問題,對對象b的cname屬性采取了新開辟內存的方式避免了內存歸屬不清所導致析構釋放空間時候的錯誤,最後我必須提一下,對于上面的程序我的解釋並不多,就是希望讀者本身運行程序觀察變化,進而深刻理解。 拷貝和淺拷貝的定義可以簡單理解成:假如一個類擁有資源(堆,或者是其它系統資源),當這個類的對象發生複制過程的時候,這個過程就可以叫做深拷貝,反之對象存在資源但複制過程並未複制資源的情況視爲淺拷貝。

淺拷貝資源後在釋放資源的時候會産生資源歸屬不清的情況導致程序運行出錯,這點尤其需要注重!以前我們的教程中討論過函數返回對象産生臨時變量的問題,接下來我們來看一下在函數中返回自定義類型對象是否也遵循此規則産生臨時對象!


C++類對象的複制-拷貝構造函數
更多內容請看C/C++技術專題專題,或

先運行下列代碼:#include <iostream>

using namespace std;

class Internet

{

public:

Internet()

{

};

Internet(char *name,char *address)

{

cout<<"載入構造函數"<<endl;

strcpy(Internet::name,name);

}

Internet(Internet &temp)

{

cout<<"載入COPY構造函數"<<endl;

strcpy(Internet::name,temp.name);

cin.get();

}

~Internet()

{

cout<<"載入析構函數!";

cin.get();

}

protected:

char name[20];

char address[20];

};

Internet tp()

{

Internet b("中國軟件開發實驗室","www.cndev-lab.com");

return b;

}

void main()

{

Internet a;

a=tp();

}從上面的代碼運行結果可以看出,程序一共載入過析構函數三次,證實了由函數返回自定義類型對象同樣會産生臨時變量,事實上對象a得到的就是這個臨時Internet類類型對象temp的值。這一下節的內容我們來說一下無名對象。 利用無名對象初始化對象系統不會不調用拷貝構造函數。

那麽什麽又是無名對象呢?

很簡單,假如在上面程序的main函數中有:

Internet("中國軟件開發實驗室","www.cndev-lab.com");

這樣的一句語句就會産生一個無名對象,無名對象會調用構造函數但利用無名對象初始化對象系統不會不調用拷貝構造函數!

下面三段代碼是很見到的三種利用無名對象初始化對象的例子。#include <iostream>

using namespace std;

class Internet

{

public:

Internet(char *name,char *address)

{

cout<<"載入構造函數"<<endl;

strcpy(Internet::name,name);

}

Internet(Internet &temp)

{

cout<<"載入COPY構造函數"<<endl;

strcpy(Internet::name,temp.name);

cin.get();

}

~Internet()

{

cout<<"載入析構函數!";

}

public:

char name[20];

char address[20];

};

void main()

{

Internet a=Internet("中國軟件開發實驗室","www.cndev-lab.com");

cout<<a.name;

cin.get();

}上面代碼的運行結果有點「出人意料」,從思維邏輯上說,當無名對象創建了後,是應該調用自定義拷貝構造函數,或者是默認拷貝構造函數來完成複制過程的,但事實上系統並沒有這麽做,因爲無名對象使用過後在整個程序中就失去了作用,對于這種情況c++會把代碼看成是: Internet a("中國軟件開發實驗室",www.cndev-lab.com); 省略了創建無名對象這一過程,所以說不會調用拷貝構造函數。


C++類對象的複制-拷貝構造函數
更多內容請看C/C++技術專題專題,或

最後讓我們來看看引用無名對象的情況。#include <iostream>

using namespace std;

class Internet

{

public:

Internet(char *name,char *address)

{

cout<<"載入構造函數"<<endl;

strcpy(Internet::name,name);

}

Internet(Internet &temp)

{

cout<<"載入COPY構造函數"<<endl;

strcpy(Internet::name,temp.name);

cin.get();

}

~Internet()

{

cout<<"載入析構函數!";

}

public:

char name[20];

char address[20];

};

void main()

{

Internet &a=Internet("中國軟件開發實驗室","www.cndev-lab.com");

cout<<a.name;

cin.get();

}引用本身是對象的別名,和複制並沒有關系,所以不會調用拷貝構造函數,但要注重的是,在c++看來:

Internet &a=Internet("中國軟件開發實驗室","www.cndev-lab.com");

是等價與:

Internet a("中國軟件開發實驗室","www.cndev-lab.com");

的,注重觀察調用析構函數的位置(這種情況是在main()外調用,而無名對象本身是在main()內析構的)。

C++類對象的複制-拷貝構造函數
更多內容請看C/C++技術專題專題,或

  在學習這一章內容前我們已經學習過了類的構造函數和析構函數的相關知識,對于普通類型的對象來說,他們之間的複制是很簡單的,例如: int a = 10; int b =a;   自己定義的類的對象同樣是對象,誰也不能阻止我們用以下的方式進行複制,例如:#include <iostream> using namespace std; class Test { public: Test(int temp) { p1=temp; } PRotected: int p1; }; void main() { Test a(99); Test b=a; }  普通對象和類對象同爲對象,他們之間的特性有相似之處也有不同之處,類對象內部存在成員變量,而普通對象是沒有的,當同樣的複制方法發生在不同的對象上的時候,那麽系統對他們進行的操作也是不一樣的,就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個複制過程的,在上面的代碼中,我們並沒有看到拷貝構造函數,同樣完成了複制工作,這又是爲什麽呢?因爲當一個類沒有自定義的拷貝構造函數的時候系統會自動提供一個默認的拷貝構造函數,來完成複制工作。   下面,我們爲了說明情況,就普通情況而言(以上面的代碼爲例),我們來自己定義一個與系統默認拷貝構造函數一樣的拷貝構造函數,看看它的內部是如何工作的!   代碼如下:#include <iostream> using namespace std; class Test { public: Test(int temp) { p1=temp; } Test(Test &c_t)//這裏就是自定義的拷貝構造函數 { cout<<"進入copy構造函數"<<endl; p1=c_t.p1;//這句假如去掉就不能完成複制工作了,此句複制過程的核心語句 } public: int p1; }; void main() { Test a(99); Test b=a; cout<<b.p1; cin.get(); }  上面代碼中的Test(Test &c_t)就是我們自定義的拷貝構造函數,拷貝構造函數的名稱必須與類名稱一致,函數的形式參數是本類型的一個引用變量,且必須是引用。   當用一個已經初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用,假如你沒有自定義拷貝構造函數的時候系統將會提供給一個默認的拷貝構造函數來完成這個過程,上面代碼的複制核心語句就是通過Test(Test &c_t)拷貝構造函數內的p1=c_t.p1;語句完成的。假如取掉這句代碼,那麽b對象的p1屬性將得到一個未知的隨機值; [url=/bbs/detail_1785268.html][img]http://image.wangchao.net.cn/it/1323424076909.gif[/img][/url] 更多內容請看C/C++技術專題專題,或   下面我們來討論一下關于淺拷貝和深拷貝的問題。   就上面的代碼情況而言,很多人會問到,既然系統會自動提供一個默認的拷貝構造函數來處理複制,那麽我們沒有意義要去自定義拷貝構造函數呀,對,就普通情況而言這的確是沒有必要的,但在某寫狀況下,類體內的成員是需要開辟動態開辟堆內存的,假如我們不自定義拷貝構造函數而讓系統自己處理,那麽就會導致堆內存的所屬權産生混亂,試想一下,已經開辟的一端堆地址原來是屬于對象a的,由于複制過程發生,b對象取得是a已經開辟的堆地址,一旦程序産生析構,釋放堆的時候,計算機是不可能清楚這段地址是真正屬于誰的,當連續發生兩次析構的時候就出現了運行錯誤。   爲了更具體的說明問題,請看如下的代碼。 #include <iostream> using namespace std; class Internet { public: Internet(char *name,char *address) { cout<<"載入構造函數"<<endl; strcpy(Internet::name,name); strcpy(Internet::address,address); cname=new char[strlen(name)+1]; if(cname!=NULL) { strcpy(Internet::cname,name); } } Internet(Internet &temp) { cout<<"載入COPY構造函數"<<endl; strcpy(Internet::name,temp.name); strcpy(Internet::address,temp.address); cname=new char[strlen(name)+1];//這裏注重,深拷貝的體現! if(cname!=NULL) { strcpy(Internet::cname,name); } } ~Internet() { cout<<"載入析構函數!"; delete[] cname; cin.get(); } void show(); protected: char name[20]; char address[30]; char *cname; }; void Internet::show() { cout<<name<<":"<<address<<cname<<endl; } void test(Internet ts) { cout<<"載入test函數"<<endl; } void main() { Internet a("中國軟件開發實驗室","www.cndev-lab.com"); Internet b = a; b.show(); test(b); }  上面代碼就演示了深拷貝的問題,對對象b的cname屬性采取了新開辟內存的方式避免了內存歸屬不清所導致析構釋放空間時候的錯誤,最後我必須提一下,對于上面的程序我的解釋並不多,就是希望讀者本身運行程序觀察變化,進而深刻理解。   拷貝和淺拷貝的定義可以簡單理解成:假如一個類擁有資源(堆,或者是其它系統資源),當這個類的對象發生複制過程的時候,這個過程就可以叫做深拷貝,反之對象存在資源但複制過程並未複制資源的情況視爲淺拷貝。   淺拷貝資源後在釋放資源的時候會産生資源歸屬不清的情況導致程序運行出錯,這點尤其需要注重!  以前我們的教程中討論過函數返回對象産生臨時變量的問題,接下來我們來看一下在函數中返回自定義類型對象是否也遵循此規則産生臨時對象! [url=/bbs/detail_1785268.html][img]http://image.wangchao.net.cn/it/1323424076941.gif[/img][/url] 更多內容請看C/C++技術專題專題,或   先運行下列代碼:#include <iostream> using namespace std; class Internet { public: Internet() { }; Internet(char *name,char *address) { cout<<"載入構造函數"<<endl; strcpy(Internet::name,name); } Internet(Internet &temp) { cout<<"載入COPY構造函數"<<endl; strcpy(Internet::name,temp.name); cin.get(); } ~Internet() { cout<<"載入析構函數!"; cin.get(); } protected: char name[20]; char address[20]; }; Internet tp() { Internet b("中國軟件開發實驗室","www.cndev-lab.com"); return b; } void main() { Internet a; a=tp(); }  從上面的代碼運行結果可以看出,程序一共載入過析構函數三次,證實了由函數返回自定義類型對象同樣會産生臨時變量,事實上對象a得到的就是這個臨時Internet類類型對象temp的值。  這一下節的內容我們來說一下無名對象。   利用無名對象初始化對象系統不會不調用拷貝構造函數。   那麽什麽又是無名對象呢?   很簡單,假如在上面程序的main函數中有:   Internet ("中國軟件開發實驗室","www.cndev-lab.com");   這樣的一句語句就會産生一個無名對象,無名對象會調用構造函數但利用無名對象初始化對象系統不會不調用拷貝構造函數!   下面三段代碼是很見到的三種利用無名對象初始化對象的例子。#include <iostream> using namespace std; class Internet { public: Internet(char *name,char *address) { cout<<"載入構造函數"<<endl; strcpy(Internet::name,name); } Internet(Internet &temp) { cout<<"載入COPY構造函數"<<endl; strcpy(Internet::name,temp.name); cin.get(); } ~Internet() { cout<<"載入析構函數!"; } public: char name[20]; char address[20]; }; void main() { Internet a=Internet("中國軟件開發實驗室","www.cndev-lab.com"); cout<<a.name; cin.get(); }  上面代碼的運行結果有點「出人意料」,從思維邏輯上說,當無名對象創建了後,是應該調用自定義拷貝構造函數,或者是默認拷貝構造函數來完成複制過程的,但事實上系統並沒有這麽做,因爲無名對象使用過後在整個程序中就失去了作用,對于這種情況c++會把代碼看成是: Internet a("中國軟件開發實驗室",www.cndev-lab.com);   省略了創建無名對象這一過程,所以說不會調用拷貝構造函數。 [url=/bbs/detail_1785268.html][img]http://image.wangchao.net.cn/it/1323424076989.gif[/img][/url] 更多內容請看C/C++技術專題專題,或   最後讓我們來看看引用無名對象的情況。#include <iostream> using namespace std; class Internet { public: Internet(char *name,char *address) { cout<<"載入構造函數"<<endl; strcpy(Internet::name,name); } Internet(Internet &temp) { cout<<"載入COPY構造函數"<<endl; strcpy(Internet::name,temp.name); cin.get(); } ~Internet() { cout<<"載入析構函數!"; } public: char name[20]; char address[20]; }; void main() { Internet &a=Internet("中國軟件開發實驗室","www.cndev-lab.com"); cout<<a.name; cin.get(); }  引用本身是對象的別名,和複制並沒有關系,所以不會調用拷貝構造函數,但要注重的是,在c++看來: Internet &a=Internet("中國軟件開發實驗室","www.cndev-lab.com");   是等價與: Internet a("中國軟件開發實驗室","www.cndev-lab.com");   的,注重觀察調用析構函數的位置(這種情況是在main()外調用,而無名對象本身是在main()內析構的)。 [url=/bbs/detail_1785268.html][img]http://image.wangchao.net.cn/it/1323424077005.gif[/img][/url] 更多內容請看C/C++技術專題專題,或
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有