 C++中的深拷(kao)貝(bei)與淺(qian)拷(kao)貝(bei)
							時間:2018-09-27      來源:未知
							C++中的深拷(kao)貝(bei)與淺(qian)拷(kao)貝(bei)
							時間:2018-09-27      來源:未知 
							說(shuo)道c++,大(da)家第一印(yin)象(xiang)(xiang)(xiang)就(jiu)(jiu)(jiu)是(shi)面向對(dui)象(xiang)(xiang)(xiang)這(zhe)四個(ge)(ge)字(zi)。當(dang)我們把一個(ge)(ge)抽象(xiang)(xiang)(xiang)的(de)(de)(de)(de)(de)類(lei)描述完(wan)畢,該有的(de)(de)(de)(de)(de)功能(neng)都有的(de)(de)(de)(de)(de)時候,接下來要做(zuo)的(de)(de)(de)(de)(de)事情就(jiu)(jiu)(jiu)是(shi)去把這(zhe)個(ge)(ge)類(lei)實例(li)化(hua)成(cheng)對(dui)象(xiang)(xiang)(xiang),換成(cheng)人話就(jiu)(jiu)(jiu)是(shi)創建(jian)一個(ge)(ge)對(dui)象(xiang)(xiang)(xiang)。這(zhe)個(ge)(ge)對(dui)象(xiang)(xiang)(xiang)的(de)(de)(de)(de)(de)類(lei)型 就(jiu)(jiu)(jiu)是(shi)用于(yu)實例(li)化(hua)這(zhe)個(ge)(ge)對(dui)象(xiang)(xiang)(xiang)的(de)(de)(de)(de)(de)基類(lei)的(de)(de)(de)(de)(de)類(lei)型。舉個(ge)(ge)栗(li)子(zi),在c語言(yan)中(zhong),我們想(xiang)要定義一個(ge)(ge)整型變(bian)(bian)量(liang)(liang),首先要寫出(chu)它(ta)的(de)(de)(de)(de)(de)基類(lei)型int,然后寫出(chu)你(ni)想(xiang)給出(chu)這(zhe)個(ge)(ge)變(bian)(bian)量(liang)(liang)的(de)(de)(de)(de)(de)名字(zi)int a=888;此時一個(ge)(ge)你(ni)想(xiang)象(xiang)(xiang)(xiang)中(zhong)的(de)(de)(de)(de)(de)整型變(bian)(bian)量(liang)(liang)就(jiu)(jiu)(jiu)出(chu)來了(le)(le)。內存中(zhong)就(jiu)(jiu)(jiu)有一個(ge)(ge)大(da)小為4個(ge)(ge)字(zi)節(jie)的(de)(de)(de)(de)(de)空間,名字(zi)叫做(zuo)a的(de)(de)(de)(de)(de)家伙。它(ta)的(de)(de)(de)(de)(de)值是(shi)888。然后我們就(jiu)(jiu)(jiu)可以在這(zhe)個(ge)(ge)變(bian)(bian)量(liang)(liang)的(de)(de)(de)(de)(de)生存周期里面使(shi)用它(ta)了(le)(le)。
本文引用地址://fsbing.cn/emb/Column/7237.html
一個(ge)對象也可以這么理解(jie),假如有類(lei)class T ,則我們可以利(li)用該類(lei)去定義一個(ge)對象,T a,你看 基類(lei)型此時(shi)叫T ,變量名叫a。是(shi)不(bu)(bu)是(shi)和定義一個(ge)整(zheng)型變量差不(bu)(bu)多?既然如此,那么是(shi)不(bu)(bu)是(shi)整(zheng)型變量的(de)賦(fu)值操作也同(tong)樣可以用在對象的(de)賦(fu)值呢?
首先來看下整型變(bian)量的賦值:
int a = 10;
int b = a;
好了,就是這么簡單(dan)明了。把(ba)a變量(liang)所對應內存空間(jian)中(zhong)的(de)那(nei)個(ge)888常量(liang)復制到(dao)變量(liang)b所在的(de)內存空間(jian)中(zhong)。然后我(wo)們(men)再累看下對象的(de)賦值:
首先有(you)個(ge)類 class Meizi,妹子類。嗯(ng),沒錯,程序員(yuan)找(zhao)女(nv)朋友都很(hen)困難,所以只能靠C++去(qu)虛擬出來(lai)。描述這個(ge)妹子的(de)方法(fa)和屬性我就(jiu)不(bu)多說(shuo)了(le),大(da)家的(de)想(xiang)象力反(fan)正都很(hen)豐(feng)富,各種size自己選(xuan)擇。這時,需要將這個(ge)具體的(de)妹子創(chuang)造出來(lai),因此(ci)有(you)了(le)以下的(de)代碼
class Meizi
{
public:
Meizi(int m_weight, float m_high, int m_age);
~Meizi();
void show()
{
cout << "this girl " << weight << "kg," << high << "cm,age" << age << endl;
}
private:
int weight;
float high;
int age;
};
Meizi::Meizi(int m_weight, float m_high, int m_age)
{
weight = m_weight;
high = m_high;
age = m_age;
}
Meizi::~Meizi()
{
}
int main()
{
Meizi Aoi_sola(45,155.4,33);
Aoi_sola.show();
return 0;
}
輸出為
  
可以看到一(yi)(yi)(yi)個(ge)對(dui)象(xiang)中的(de)結(jie)構比一(yi)(yi)(yi)個(ge)單(dan)純(chun)的(de)整(zheng)型變(bian)量要復雜,存(cun)在(zai)有各(ge)種(zhong)成員變(bian)量。為(wei)(wei)了實現將一(yi)(yi)(yi)個(ge)類實例化(hua)成為(wei)(wei)一(yi)(yi)(yi)個(ge)對(dui)象(xiang),我(wo)們都(dou)知道會用到一(yi)(yi)(yi)種(zhong)叫做構造函(han)數的(de)東(dong)西,把該分(fen)(fen)配的(de)資源給(gei)分(fen)(fen)配了。現在(zai)想(xiang)要用這樣一(yi)(yi)(yi)個(ge)頗為(wei)(wei)復雜的(de)對(dui)象(xiang)去給(gei)另一(yi)(yi)(yi)個(ge)對(dui)象(xiang)賦值(zhi)的(de)話(hua),能否像(xiang)之前整(zheng)型變(bian)量賦值(zhi)一(yi)(yi)(yi)樣呢?現在(zai),就(jiu)用這個(ge)剛剛出(chu)爐的(de)妹(mei)子(zi)復制成另一(yi)(yi)(yi)個(ge)妹(mei)子(zi),主程序改成這個(ge)樣子(zi)
int main()
{
Meizi Aoi_sola(45,155.4,33);
Aoi_sola.show();
Meizi Yoshizawa_Akiho = Aoi_sola;
Yoshizawa_Akiho.show();
return 0;
}
結果是
  
居然成功了,我們用(yong)了一(yi)(yi)個妹子去創造(zao)出了另一(yi)(yi)個。這(zhe)個里(li)面我們將(jiang)原先那(nei)個Aoi_sola對(dui)象(xiang)的(de)內(nei)容完完全全復制(zhi)給了新(xin)對(dui)象(xiang)Yoshizawa_Akiho,那(nei)么在實現的(de)過程(cheng)中,我們的(de)c++編譯器(qi)會調用(yong)一(yi)(yi)個看不見的(de)東西(xi),他叫做默認拷(kao)貝構(gou)造(zao)函(han)(han)數。這(zhe)個函(han)(han)數為新(xin)妹子Yoshizawa_Akiho分配了內(nei)存空(kong)間并完成了與妹子Aoi_sola的(de)復制(zhi)過程(cheng),克隆人就(jiu)這(zhe)么出來(lai)了。
拷貝構造函數(shu)是怎么工作(zuo)的?怎么就(jiu)能(neng)分(fen)配內存且復制成員了(le)呢?向(xiang)下看
class Meizi
{
public:
Meizi(int m_weight, float m_high, int m_age);
Meizi(Meizi &M);
~Meizi();
void show()
{
cout << "this girl " << weight << "kg," << high << "cm,age" << age << endl;
}
private:
int weight;
float high;
int age;
};
Meizi::Meizi(int m_weight, float m_high, int m_age)
{
weight = m_weight;
high = m_high;
age = m_age;
}
Meizi::Meizi(Meizi &M)//<-----看這里(li)看這里(li)
{
weight = M.weight;
high = M.high;
age = M.age;
cout << "調(diao)用拷(kao)貝構造" << endl;
}
Meizi::~Meizi()
{
}
int main()
{
Meizi Aoi_sola(45,155.4,33);
Aoi_sola.show();
Meizi Yoshizawa_Akiho = Aoi_sola;
Yoshizawa_Akiho.show();
return 0;
}
然后我們運行下(xia)
  
看到了么,在去創建新對象(xiang)Yoshizawa_Akiho的(de)(de)時(shi)候調(diao)用了我們(men)自己定(ding)義的(de)(de)一(yi)個(ge)函(han)數(shu)(shu),這個(ge)函(han)數(shu)(shu)和(he)類(lei)(lei)名(ming)同(tong)名(ming),沒有返回值類(lei)(lei)型,參數(shu)(shu)是該類(lei)(lei)的(de)(de)一(yi)個(ge)引用。我們(men)不用顯式的(de)(de)調(diao)用它,因為在用一(yi)個(ge)已經初(chu)始化(hua)(hua)過了的(de)(de)自定(ding)義類(lei)(lei)類(lei)(lei)型對象(xiang)去初(chu)始化(hua)(hua)一(yi)個(ge)新對象(xiang)的(de)(de)時(shi)候拷(kao)(kao)貝(bei)構(gou)造函(han)數(shu)(shu)會被自動的(de)(de)調(diao)用,當類(lei)(lei)的(de)(de)對象(xiang)需要拷(kao)(kao)貝(bei)時(shi),拷(kao)(kao)貝(bei)構(gou)造函(han)數(shu)(shu)將會被調(diao)用。以下情況都會調(diao)用拷(kao)(kao)貝(bei)構(gou)造函(han)數(shu)(shu):
(1)一個(ge)對象(xiang)以值傳(chuan)遞的方(fang)式傳(chuan)入(ru)函數體
(2)一個對象以值傳遞(di)的方式(shi)從函數返回
(3)一(yi)個(ge)對象(xiang)(xiang)需要通過另外一(yi)個(ge)對象(xiang)(xiang)進行初始化。
如(ru)果在類中沒有顯式地聲明一個(ge)拷貝構造(zao)函(han)(han)數,那么,編譯器將(jiang)會自動生(sheng)成一個(ge)默認的(de)拷貝構造(zao)函(han)(han)數,該構造(zao)函(han)(han)數完(wan)成對象(xiang)之間的(de)位拷貝。位拷貝又稱淺拷貝。
淺(qian)淺(qian)的拷(kao)貝(bei)一(yi)下,就(jiu)能將一(yi)個新對(dui)象(xiang)(妹子(zi))創建(jian)出來(lai),方便又快捷。想(xiang)想(xiang)還有些小激動呢。
沒錯,接下(xia)來(lai)我要說(shuo)可是(shi),在某些(xie)狀況下(xia),類內(nei)(nei)成(cheng)員變(bian)量(liang)需要動態開辟堆內(nei)(nei)存,如(ru)果實(shi)行淺拷貝(bei),也就(jiu)是(shi)把(ba)對(dui)象(xiang)(xiang)里的(de)值完全復制給(gei)另一(yi)(yi)個(ge)(ge)對(dui)象(xiang)(xiang),如(ru)A=B。這(zhe)時(shi),如(ru)果B中(zhong)有一(yi)(yi)個(ge)(ge)成(cheng)員變(bian)量(liang)指針(zhen)已經申請了(le)內(nei)(nei)存,那A中(zhong)的(de)那個(ge)(ge)成(cheng)員變(bian)量(liang)也指向(xiang)同一(yi)(yi)塊內(nei)(nei)存。這(zhe)就(jiu)出現了(le)問題:當(dang)B把(ba)內(nei)(nei)存釋(shi)放(fang)了(le)(如(ru):析構),這(zhe)時(shi)A內(nei)(nei)的(de)指針(zhen)就(jiu)是(shi)野指針(zhen)了(le),出現運行錯誤。不信?我們(men)來(lai)試試看,還(huan)是(shi)這(zhe)兩個(ge)(ge)妹(mei)子,Aoi_sola和Yoshizawa_Akiho,
class Meizi
{
public:
Meizi(int m_weight, float m_high, int m_age,char *home);
~Meizi();
void show()
{
  cout << "this girl " << weight << "kg," << high << "cm,age" << age <<"home"<
}
private:
int weight;
float high;
int age;
char *home;
};
Meizi::Meizi(int m_weight, float m_high, int m_age,char *m_home)
{
weight = m_weight;
high = m_high;
age = m_age;
home = new char[20];
strcpy(home,m_home);
}
Meizi::~Meizi()
{
}
int main()
{
Meizi Aoi_sola(45,155.4,33,"Tokyo");
Aoi_sola.show();
Meizi Yoshizawa_Akiho = Aoi_sola;
Yoshizawa_Akiho.show();
return 0;
}
新添加(jia)了妹子(zi)的家鄉(xiang)成員變量 home,是一個字符串。之后我(wo)們采取默認拷貝(bei)構造函(han)數來初始(shi)化新對象,結(jie)果:
  
錯了。
原因在于構(gou)造(zao)函(han)數(shu)中我們(men)利用(yong)malloc動態開(kai)辟了(le)(le)一(yi)段堆空間(jian),此時(shi)對(dui)(dui)象Aoi_sola的(de)成員(yuan)變量(liang)指(zhi)針(zhen)home指(zhi)向了(le)(le)該空間(jian),利用(yong)默認構(gou)造(zao)函(han)數(shu)初始化新(xin)對(dui)(dui)象Yoshizawa_Akiho,這個時(shi)候你知道(dao)是復(fu)制操作,因此Yoshizawa_Akiho對(dui)(dui)象的(de)成員(yuan)變量(liang)home指(zhi)針(zhen)也(ye)指(zhi)向的(de)和原先(xian)對(dui)(dui)象相(xiang)同(tong)(tong)的(de)地方,在析(xi)(xi)構(gou)的(de)時(shi)候,我們(men)有個好習(xi)慣,手(shou)動分配的(de)資源要(yao)手(shou)動釋放,結(jie)果我們(men)將(jiang)兩(liang)(liang)個對(dui)(dui)象析(xi)(xi)構(gou)的(de)時(shi)候調(diao)用(yong)了(le)(le)兩(liang)(liang)次(ci)析(xi)(xi)構(gou)函(han)數(shu)也(ye)就是調(diao)用(yong)了(le)(le)兩(liang)(liang)次(ci)delete,也(ye)就是將(jiang)同(tong)(tong)一(yi)塊內(nei)存釋放了(le)(le)兩(liang)(liang)次(ci)。結(jie)果當(dang)然(ran)出錯了(le)(le)。針(zhen)對(dui)(dui)這種情況,我們(men)就需(xu)要(yao)深拷貝(bei)構(gou)造(zao)函(han)數(shu),多深呢?請看代碼
class Meizi
{
public:
Meizi(int m_weight, float m_high, int m_age,char *home);
Meizi(Meizi &M) //這里(li)就是我(wo)們自己定(ding)義的拷(kao)貝構(gou)造函數
{
weight = M.weight;
high = M.high;
age = M.age;
home = new char[20];
if (home != NULL)
{
strcpy(home,M.home);
}
cout << "copy instructor" << endl;
//深刻的拷貝構造(zao)了一(yi)下(xia)
}
~Meizi();
void show()
{
cout << "this girl " << weight << "kg," << high << "cm,age" << age << endl;
}
private:
int weight;
float high;
int age;
char *home;
};
Meizi::Meizi(int m_weight, float m_high, int m_age,char *m_home)
{
weight = m_weight;
high = m_high;
age = m_age;
home = new char[20];
strcpy(home,m_home);
}
Meizi::~Meizi()
{
delete home;
}
int main()
{
Meizi Aoi_sola(45,155.4,33,"Tokyo");
Aoi_sola.show();
Meizi Yoshizawa_Akiho = Aoi_sola;
Yoshizawa_Akiho.show();
return 0;
}
結果:
  
所以(yi)原(yuan)先的對(dui)(dui)象去創建新對(dui)(dui)象的時候會調(diao)用我們自己(ji)(ji)定義的拷貝構(gou)造(zao)函數,在自定義拷貝構(gou)造(zao)函數中我們自己(ji)(ji)手動分配了內存空間(jian)。進(jin)而實現的拷貝構(gou)造(zao),叫做深拷貝。
那么問題來了,這(zhe)兩(liang)種(zhong)拷(kao)(kao)(kao)貝(bei)(bei)構造(zao)我應該(gai)怎么選呢?什么時候(hou)用(yong)淺(qian)拷(kao)(kao)(kao)貝(bei)(bei)什么時候(hou)用(yong)深拷(kao)(kao)(kao)貝(bei)(bei)?總(zong)的來說還是(shi)要(yao)根據類的實現(xian)來判斷該(gai)用(yong)淺(qian)拷(kao)(kao)(kao)貝(bei)(bei)還是(shi)深拷(kao)(kao)(kao)貝(bei)(bei)。如果(guo)需(xu)要(yao)拷(kao)(kao)(kao)貝(bei)(bei)這(zhe)個對(dui)象引(yin)用(yong)的對(dui)象,則(ze)是(shi)深拷(kao)(kao)(kao)貝(bei)(bei),否則(ze)是(shi)淺(qian)拷(kao)(kao)(kao)貝(bei)(bei)。

