C++復制構造函數,一般在一下幾種情況中出現
1:對象以值傳遞的方式傳入函數時
2:對象以值傳遞的的方式從函數返回
3:一個對象通過另一個對象初始化
4:數組:根據數組中的初始化列表初始化數組的時候。
5:容器:初始化順序容器中的元素
有3種情況必須使用復制構造函數:
A:如果有數據成員是指針
B:要在構造函數中分配其它資源
C:在創建對象是必須做一些另外的工作
對3進行一些解釋:
如果一個類中的成員變量有引用數據類型,這時候就要特別注意了,比如在構造函數中是new出來的,那麼在復制構造函數中最好還是要new。
下面是一個例子,如果在復制構造函數中不new一個數組,那麼就要報錯(自己寫了釋放str的析構函數),這個例子如果在自己寫了析構函數來進行釋放str的內存,或則想實現深層復制,給str重新分配空間,而不是把地址直接賦值給str,也是必須使用默認構造函數的例子。
[cpp]
#include <iostream>
#include <cstring>
#include "deep_copy.h"
using namespace std;
class DeepCopy{
private:
int a;
char *str;
public:
DeepCopy(int aint,const char *str1):str(0)
{
a=aint;
str=new char[50];
strcpy(str,str1);
}
DeepCopy(const DeepCopy& dc):str(0)
{
a=dc.a;
if(str==NULL)
str=new char[50];
strcpy(str,dc.str);
}
~DeepCopy()
{
cout<<"~deepcopy**********"<<endl;
if(str!=NULL)
delete str;
str=NULL;
}
void set(const char* setStr)
{
strcpy(str,setStr);
}
void show()
{
cout<<"a="<<a<<"\nstr="<<str<<endl;
}
};
int main()
{
DeepCopy dp(10,"i love you ~~~~~~~");
dp.show();
DeepCopy dp1=dp;
dp1.show();
cout<<"modify the dp1,check if the dp will change!!! "<<endl;
dp1.set("i hate you!!!");
dp.show();
dp1.show();
}
一下是下面的關於c++深層拷貝的一些問題,我始終覺得有點問題
C++拷貝構造函數(深拷貝,淺拷貝)
對於普通類型的對象來說,它們之間的復制是很簡單的,例如:
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的復制過程。就類對象而言,相同類型的類對象是通過拷貝構造函數來完成整個復制過程的。下面舉例說明拷貝構造函數的工作過程。
#include <iostream>
using namespace std;
class CExample {
private:
int a;
public:
CExample(int b)
{ a=b;}
CExample(const CExample& C)
{
a=C.a;
}
void Show ()
{
cout<<a<<endl;
}
};
int main()
{
CExample A(100);
CExample B=A;
B.Show ();
return 0;
}
CExample(constCExample& 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;語句完成的。
摘自 SongCdut的專欄