對于普通類型的對象來說,它們之間的複制是很簡單的,例如:
int a=88;
int b=a;
而類對象與普通對象不同,類對象内部結構一般較為複雜,存在各種成員變量。下面看一個類對象拷貝的簡單例子。
#include <iostream>
using namespace std;
class cexample {
private:
int a;
public:
cexample(int b)
{ a=b;}
void show ()
{
cout<<a<<endl;
}
};
int main()
{
cexample a(100);
cexample b=a;
b.show ();
return 0;
}
運作程式,螢幕輸出100。從以上代碼的運作結果可以看出,系統為對象b配置設定了記憶體并完成了與對象a的複制過程。就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個複制過程的。下面舉例說明拷貝構造函數的工作過程。
int a;
cexample(int b)
{ a=b;}
cexample(const cexample& c)
{
a=c.a;
void show ()
cexample a(100);
cexample b=a;
b.show ();
return 0;
cexample(const cexample&
c)就是我們自定義的拷貝構造函數。可見,拷貝構造函數是一種特殊的構造函數,函數的名稱必須和類名稱一緻,它的唯一的一個參數是本類型的一個引用變量,該參數是const類型,不可變的。例如:類x的拷貝構造函數的形式為x(x&
x)。
當用一個已初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用。也就是說,當類的對象需要拷貝時,拷貝構造函數将會被調用。以下情況都會調用拷貝構造函數:
一個對象以值傳遞的方式傳入函數體
一個對象以值傳遞的方式從函數傳回
一個對象需要通過另外一個對象進行初始化。
如果在類中沒有顯式地聲明一個拷貝構造函數,那麼,編譯器将會自動生成一個預設的拷貝構造函數,該構造函數完成對象之間的位拷貝。位拷貝又稱淺拷貝,後面将進行說明。
自定義拷貝構造函數是一種良好的程式設計風格,它可以阻止編譯器形成預設的拷貝構造函數,提高源碼效率。
淺拷貝和深拷貝
在某些狀況下,類内成員變量需要動态開辟堆記憶體,如果實行位拷貝,也就是把對象裡的值完全複制給另一個對象,如a=b。這時,如果b中有一個成員變量指針已經申請了記憶體,那a中的那個成員變量也指向同一塊記憶體。這就出現了問題:當b把記憶體釋放了(如:析構),這時a内的指針就是野指針了,出現運作錯誤。
深拷貝和淺拷貝可以簡單了解為:如果一個類擁有資源,當這個類的對象發生複制過程的時候,資源重新配置設定,這個過程就是深拷貝,反之,沒有重新配置設定資源,就是淺拷貝。下面舉個深拷貝的例子。
#include <iostream>
using
namespace std;
class ca
public:
ca(int b,char*
cstr)
{
a=b;
str=new
char[b];
strcpy(str,cstr);
}
ca(const ca&
c)
a=c.a;
str=new char[a];
//深拷貝
if(str!=0)
strcpy(str,c.str);
void
show()
cout<<str<<endl;
~ca()
delete
str;
private:
int a;
char *str;
int
main()
ca a(10,"hello!");
ca b=a;
b.show();
return
0;
深拷貝和淺拷貝的定義可以簡單了解成:如果一個類擁有資源(堆,或者是其它系統資源),當這個類的對象發生複制過程的時候,這個過程就可以叫做深拷貝,反之對象存在資源,但複制過程并未複制資源的情況視為淺拷貝。
淺拷貝資源後在釋放資源的時候會産生資源歸屬不清的情況導緻程式運作出錯。
test(test
&c_t)是自定義的拷貝構造函數,拷貝構造函數的名稱必須與類名稱一緻,函數的形式參數是本類型的一個引用變量,且必須是引用。
當用一個已經初始化過了的自定義類類型對象去初始化另一個新構造的對象的時候,拷貝構造函數就會被自動調用,如果你沒有自定義拷貝構造函數的時候,系統将會提供給一個預設的拷貝構造函數來完成這個過程,上面代碼的複制核心語句就是通過test(test
&c_t)拷貝構造函數内的p1=c_t.p1;語句完成的。