複製構造函式

hyon hyon hyon

複製構造函式(直接初始化、複製初始化、賦值、臨時對象)
使用複製構造函式前應弄清的幾個問題:何時調用複製構造函式,複製構造函式有何功能,為什麼要定義自已的複製構造函式。
1.複製構造函式:當用戶沒有定義自已的複製構造函式時系統將生成一個默認的複製構造函式。當按值傳遞對象時,就會創建一個形參的臨時對象,然後調用複製構造函式把臨時對象的值複製給實參。
2.默認複製構造函式的功能:將一個對象的非靜態成員的值逐個複製給另一個對象,注意複製的是成員的值,這種複製方式也稱為淺複製。因為靜態成員屬於整個類,而不屬於某個對象,所以調用複製構造函式時靜態成員不會受到影響。
3.何時生成臨時對象:
情形1按值傳遞對象,注意是按值傳遞對象,按值傳遞意味著會創建一個原始對象的副本,
情形2函式返回對象時。
情形3用一個對象初始化另一個對象時,即複製初始化。語句hyong x=y和hyong x=hyong(y)這裡y是hyong類型的對象,都將調用複製構造函式。但有可能創建臨時對象,也有可能不創建臨時對象而用複製構造函式直接初始化對象,這取決於編譯器。
4.臨時對象如何產生:臨時對象是由複製構造函式創建的,當臨時對象消失時會調用相應的析構函式。也就是說,只要創建了臨時對象就會多調用一次析構函式。
5.何時使用複製構造函式:按值傳遞對象,函式返回對象,用一個對象初始化另一個對象(即複製初始化),根據元素初始化列表初始化數組元素,這四種情況都將調用複製構造函式。記住:複製構造函式只能用於初始化,不能用於賦值,賦值時不會調用複製構造函式,而是使用賦值操作符。
6.直接初始化:直接初始化,是把初始化式放在圓括弧中的。對於類類型來說,直接初始化總是調用與實參匹配的構造函式來初始化的。
7.複製初始化與複製構造函式:複製初始化使用=等於符號來初始化,複製初始化也是創建一個新對象,並且其初值來自於另一個已存在的對象,複製初始化總是調用複製構造函式來初始化的。複製初始化時,首先使用指定的構造函式創建一個臨時對象,然後用複製構造函式將臨時對象的每個非static成員依次的複製到新創建的對象,複製構造函式執行的是逐個成員初始化。注意,這裡是用一個已存在的對象創建另一個新對象,與用構造函式直接創建一個新對象不一樣,使用構造函式初始化時不會使用另一個對象。比如有類hyong,則語句hyong m(1,2)調用構造函式直接初始化,而語句hyong n=m則是用已存在的對象m去初始化一個新對象n,屬於複製初始化。
8.理解賦值與複製初始化的區別(重點)賦值是在兩個已存在的對象間進行的,也就是用一個已存在的對象去改變另一個已存在對象的值。賦值將調用賦值操作符對對象進行操作,賦值操作符將在操作符重載中講解。比如有類hyong,有語句hyong x(1);hyong y(1,2)則x=y;這就是賦值,因為對象x和y是已經存在的對象,而語句hyong x=y;則是複製初始化,是用一個已存在的對象y去創建一個新對象x,所以是複製初始化。
9.複製初始化和賦值是在兩個對象之間進行的操作,而直接初始化則不是。
10.注意:使用複製構造函式不一定創建臨時對象,就如語句hyong x=hyong(y),其中y是hyong類型的對象,就有可能不創建臨時對象,這取決於編譯器。這裡如果創建了臨時對象,則當臨時對象消亡時將調用一次析構函式;而如果沒有調用,而是直接用複製構造函式初始化對象的,就不會調用析構函式。
11.複製構造函式的形式:hyong(const hyong & obj);它接受一個指向類對象的常量引用作為參數。定義為const是必須的,因為複製構造函式只是複製對象,所以沒必要改變傳遞來的對象的值,聲明為引用可以節省時間,如果是按值傳遞的話就會生成對象的副本,會浪費資源,而引用就不會。
12.為什麼需要定義自己的複製構造函式:如果類只包含類類型成員和內置類型的成員,則可以不用顯示定義複製構造函式。但如果類中包含有指針或者有分配其他類型資源時,就必須重新定義複製構造函式。因為類中有指針成員,當把用一個對象初始化另一個對象時,這時兩個對象中的指針都指向同一段記憶體,這時如果其中一個對象被消毀了,這時對象中指針所指向的記憶體也同樣被消毀,但另一個對象確不知道這種情況,這時就會出現問題。比如hyong類中含有一個成員指針p,當聲明了hyong x=y其中y也是hyong類的對象,這時對象x和y中的指針成員p都指向同一段記憶體,而如果y被消毀,但x還沒被消毀時就會出問題,這時y中對象的成員指針p已經釋放了該記憶體資源,而x中的成員指針p還不知道已經釋放了該資源,這時就會出問題。因為對象x和y中的成員指針共享同一段記憶體,所以對y中的成員指針p的修改就會影響到對象x中的成員指針。所有這些情況都需要重定義複製構造函式來顯示的初始化成員的值,這種初始化方式也被稱為深度複製
13.如果顯式定義了複製構造函式,則調用顯式複製構造函式來直接初始化對象;如果沒有顯式定義複製構造函式,則調用默認的複製構造函式直接初始化對象。
14.注意:1.在VC++中語句hyong n=m不生成臨時對象。但如果顯式定義了複製構造函式,則調用顯式複製構造函式來直接初始化對象n,如果沒有顯式定義複製構造函式,則調用默認的複製構造函式直接初始化對象n。2.在VC++中語句hyong m1=hyong(m)有可能生成臨時對象,也有可能不生成臨時對象。如果顯式定義了複製構造函式,則用複製構造函式直接初始化對象m1,不生成臨時對象;如果沒有顯式定義複製構造函式,則複製構造函式將創造臨時對象,初始化對象m1。
15.C++自動提供的成員函式默認構造函式,複製構造函式,默認析構函式,賦值操作符,地址操作符即this指針,這五種函式如果用戶沒有定義,則系統會自動創建一個。
16.直接調用類中的構造函式:可以在類中的函式、類外的獨立函式,即main()函式中,直接調用某一個類的構造函式。比如在main函式中可以有語句n=A(4);這裡n是類A的對象,這裡就是直接調用類A的構造函式創建一個類A的臨時對象,然後把該臨時對象的值賦給類A的對象n。在類中的函式和在類外的函式調用類的構造函式的方法和這裡類似。注意語句n.A(4)是錯誤的語句,不能由對象調用類中的構造函式。
例:複製構造函式的使用
class hyong
{
public:
hyong();
hyong(int i);
hyong(const hyong& obj);
~hyong();
void h(hyong k);
hyong f();
int a,b,c;
};
hyong::hyong()
{
a=b=c=0;
cout<<"構造函式"<<"\n";
}
hyong::hyong(int i)
{
a=b=c=i;
cout<<"構造函式2"<<"\n";
}
hyong::~hyong()
{
cout<<"析構函式"<<"\n";
}
//複製構造函式
hyong::hyong(const hyong &obj)
{
a=obj.a;
b=obj.b;
c=obj.c;
cout<<"複製構造函式"<<"\n";
}
//按值傳遞對象
void hyong::h(hyong k)
{
cout<<"按值傳遞對象"<<k.a<<k.b<<"\n";
}
//返回值為對象
hyong hyong::f()
{
hyong kk(5);
return kk;
}
//以下為幾種複製初始化的方式。
int main()
{
hyong m(1);
// hyong n=m和hyong m1=hyong(m)是否生成臨時對象依編譯器而定
hyong n=m; //在VC++中此語句不生成臨時對象,調用顯式定義的複製構造函式初始化對象
cout<<m.a<<m.b<<"\n";
cout<<n.a<<n.b<<"\n";
hyong m1=hyong(m); //此語句有可能生成臨時對象,也有可能不生成臨時對象。
cout<<m1.a<<m1.b<<"\n"; //此語句不生成臨時對象,調用顯式定義的複製構造函式初始化對象m1。如果沒有顯式定義複製構造函式,則會生成臨時對象,臨時對象撤消時會調用析構函式。
hyong m2(m); cout<<m2.a<<m2.b<<"\n"; //直接調用構造函式,因此不會生成臨時對象
hyong *p=new hyong(m); cout<<p->a<<p->b<<"\n"; //不生成臨時對象,直接調用構造函式初始化。
//按值傳遞和返回對象的例子。
h(m); cout<<"kkk"<<"\n"; //按值傳遞對象m,當調用函式h時就會使用複製構造函式生成一個臨時對象,然後把這個臨時對象複製給實參,當函式調用完畢時就會撤消臨時對象,此時會調用一個析構函式,析構函式在h函式的作用域消失時才調用,也就是說在執行了h的函式體後才會調用析構函式。
hyong m4=f(); cout<<m4.a<<m4.b<<"\n"; //用返回的對象初始化對象m4,此語句沒有生成臨時對象,原因還不清楚,待考證,可能與語句是複製初始化有關。
hyong m5; m5=f(); //此語句調用函式f,f返回一個對象,在返回時會調用複製構造函式生成一個臨時對象,並把這個臨時對象作為默認賦值操作符的一個參數,因此這裡不但調用了複製構造函式還調用了賦值操作符。
cout<<m5.a<<m5.b<<"\n";
hyong m6; m6=m; //把m的值賦給m6,注意這裡不會調用複製構造函式,也不會生成臨時對象,因為這裡會把m當成是默賦值操作符的一個參數,調用的是默認賦值操作符。
}
例:直接調用類中的構造函式
class A {public: int a; A(){a=0;} A(int i){a=i;} ~A(){cout<<"xi"<<"\n";}
A f(){return A(3);} }; //在類中調用類的構造函式,當該函式被對象調用時反回由構造函式A構造的一個臨時對象。
A g() {return A(5);} //類外的函式調用類的構造函式的方法,注意,這裡是直接使用函式名的。
int main()
{ A m(1); A n(2); n=m.f(); cout<<n.a<<"\n"; //輸出3,調用類中的f函式,f函式用構造函式反回一個臨時對象。
n=g(); cout<<n.a<<"\n"; //輸出5,調用類外的函式g
n=A(6); cout<<n.a; //在main函式中直接調用構造函式創造一個臨時對象,然後把這個臨時對象的值賦給對象n。
//n.A(7); //錯誤,不能用類的對象來調用構造函式。
}

相關詞條

熱門詞條

聯絡我們