c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程_第1頁
c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程_第2頁
c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程_第3頁
c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程_第4頁
c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程_第5頁
已閱讀5頁,還剩4頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第c++詳細(xì)講解構(gòu)造函數(shù)的拷貝流程http:http:

s1、s2、s3、s4以及func()的形參str,都是使用拷貝的方式來初始化的。

對(duì)于s1、s2、s3、s4,都是將其它對(duì)象的數(shù)據(jù)拷貝給當(dāng)前對(duì)象,以完成當(dāng)前對(duì)象的初始化。

對(duì)于func()的形參str,其實(shí)在定義時(shí)就為它分配了內(nèi)存,但是此時(shí)并沒有初始化,只有等到調(diào)用func()時(shí),才會(huì)將其它對(duì)象的數(shù)據(jù)拷貝給str以完成初始化。

當(dāng)以拷貝的方式初始化一個(gè)對(duì)象時(shí),會(huì)調(diào)用一個(gè)特殊的構(gòu)造函數(shù),就是拷貝構(gòu)造函數(shù)(CopyConstructor)。

#includeiostream

#includestring

usingnamespacestd;

classStudent{

public:

Student(stringname="",intage=0,floatscore=0.0f);//普通構(gòu)造函數(shù)

Student(constStudentstu);//拷貝構(gòu)造函數(shù)(聲明)

public:

voiddisplay();

private:

stringm_name;

intm_age;

floatm_score;

Student::Student(stringname,intage,floatscore):m_name(name),m_age(age),m_score(score){}

//拷貝構(gòu)造函數(shù)(定義)

Student::Student(constStudentstu){

this-m_name=stu.m_name;

this-m_age=stu.m_age;

this-m_score=stu.m_score;

cout"Copyconstructorwascalled."endl;

voidStudent::display(){

coutm_name"的年齡是"m_age",成績(jī)是"m_scoreendl;

intmain(){

constStudentstu1("小明",16,90.5);

Studentstu2=stu1;//調(diào)用拷貝構(gòu)造函數(shù)

Studentstu3(stu1);//調(diào)用拷貝構(gòu)造函數(shù)

stu1.display();

stu2.display();

stu3.display();

return0;

}

運(yùn)行結(jié)果:

Copyconstructorwascalled.

Copyconstructorwascalled.

小明的年齡是16,成績(jī)是90.5

小明的年齡是16,成績(jī)是90.5

小明的年齡是16,成績(jī)是90.5

第8行是拷貝構(gòu)造函數(shù)的聲明,第20行是拷貝構(gòu)造函數(shù)的定義??截悩?gòu)造函數(shù)只有一個(gè)參數(shù),它的類型是當(dāng)前類的引用,而且一般都是const引用。

1)為什么必須是當(dāng)前類的引用呢?

如果拷貝構(gòu)造函數(shù)的參數(shù)不是當(dāng)前類的引用,而是當(dāng)前類的對(duì)象,那么在調(diào)用拷貝構(gòu)造函數(shù)時(shí),會(huì)將另外一個(gè)對(duì)象直接傳遞給形參,這本身就是一次拷貝,會(huì)再次調(diào)用拷貝構(gòu)造函數(shù),然后又將一個(gè)對(duì)象直接傳遞給了形參,將繼續(xù)調(diào)用拷貝構(gòu)造函數(shù)這個(gè)過程會(huì)一直持續(xù)下去,沒有盡頭,陷入死循環(huán)。

只有當(dāng)參數(shù)是當(dāng)前類的引用時(shí),才不會(huì)導(dǎo)致再次調(diào)用拷貝構(gòu)造函數(shù),這不僅是邏輯上的要求,也是C++語法的要求。

2)為什么是const引用呢?

拷貝構(gòu)造函數(shù)的目的是用其它對(duì)象的數(shù)據(jù)來初始化當(dāng)前對(duì)象,并沒有期望更改其它對(duì)象的數(shù)據(jù),添加const限制后,這個(gè)含義更加明確了。

另外一個(gè)原因是,添加const限制后,可以將const對(duì)象和非const對(duì)象傳遞給形參了,因?yàn)榉莄onst類型可以轉(zhuǎn)換為const類型。如果沒有const限制,就不能將const對(duì)象傳遞給形參,因?yàn)閏onst類型不能轉(zhuǎn)換為非const類型,這就意味著,不能使用const對(duì)象來初始化當(dāng)前對(duì)象了。

當(dāng)然,你也可以再添加一個(gè)參數(shù)為非const引用的拷貝構(gòu)造函數(shù),這樣就不會(huì)出錯(cuò)了。換句話說,一個(gè)類可以同時(shí)存在兩個(gè)拷貝構(gòu)造函數(shù),一個(gè)函數(shù)的參數(shù)為const引用,另一個(gè)函數(shù)的參數(shù)為非const引用。

classBase{

public:

Base():m_a(0),m_b(0){}

Base(inta,intb):m_a(a),m_b(b){}

private:

intm_a;

intm_b;

intmain(){

inta=10;

intb=a;//拷貝

Baseobj1(10,20);

Baseobj2=obj1;//拷貝

return0;

}

b和obj2都是以拷貝的方式初始化的,具體來說,就是將a和obj1所在內(nèi)存中的數(shù)據(jù)按照二進(jìn)制位(Bit)復(fù)制到b和obj2所在的內(nèi)存,這種默認(rèn)的拷貝行為就是淺拷貝,這和調(diào)用memcpy()函數(shù)的效果非常類似。

對(duì)于簡(jiǎn)單的類,默認(rèn)的拷貝構(gòu)造函數(shù)一般就夠用了,我們也沒有必要再顯式地定義一個(gè)功能類似的拷貝構(gòu)造函數(shù)。但是當(dāng)類持有其它資源時(shí),例如動(dòng)態(tài)分配的內(nèi)存、指向其他數(shù)據(jù)的指針等,默認(rèn)的拷貝構(gòu)造函數(shù)就不能拷貝這些資源了,我們必須顯式地定義拷貝構(gòu)造函數(shù),以完整地拷貝對(duì)象的所有數(shù)據(jù)。

下面我們通過一個(gè)具體的例子來說明顯式定義拷貝構(gòu)造函數(shù)的必要性。

#includeiostream

#includecstdlib

usingnamespacestd;

//變長(zhǎng)數(shù)組類

classArray{

public:

Array(intlen);

Array(constArrayarr);//拷貝構(gòu)造函數(shù)

~Array();

public:

intoperator[](inti)const{returnm_p[i];}//獲取元素(讀?。?/p>

intoperator[](inti){returnm_p[i];}//獲取元素(寫入)

intlength()const{returnm_len;}

private:

intm_len;

int*m_p;

Array::Array(intlen):m_len(len){

m_p=(int*)calloc(len,sizeof(int));

Array::Array(constArrayarr){//拷貝構(gòu)造函數(shù)

this-m_len=arr.m_len;

this-m_p=(int*)calloc(this-m_len,sizeof(int));

memcpy(this-m_p,arr.m_p,m_len*sizeof(int));

Array::~Array(){free(m_p);}

//打印數(shù)組元素

voidprintArray(constArrayarr){

intlen=arr.length();

for(inti=0;ii++){

if(i==len-1){

coutarr[i]endl;

}else{

coutarr[i]",";

intmain(){

Arrayarr1(10);

for(inti=0;ii++){

arr1[i]=i;

Arrayarr2=arr1;

arr2[5]=100;

arr2[3]=29;

printArray(arr1);

printArray(arr2);

return0;

}

運(yùn)行結(jié)果:

0,1,2,3,4,5,6,7,8,9

0,1,2,29,4,100,6,7,8,9

本例中我們顯式地定義了拷貝構(gòu)造函數(shù),它除了會(huì)將原有對(duì)象的所有成員變量拷貝給新對(duì)象,還會(huì)為新對(duì)象再分配一塊內(nèi)存,并將原有對(duì)象所持有的內(nèi)存也拷貝過來。這樣做的結(jié)果是,原有對(duì)象和新對(duì)象所持有的動(dòng)態(tài)內(nèi)存是相互獨(dú)立的,更改一個(gè)對(duì)象的數(shù)據(jù)不會(huì)影響另外一個(gè)對(duì)象,本例中我們更改了arr2的數(shù)據(jù),就沒有影響arr1。

這種將對(duì)象所持有的其它資源一并拷貝的行為叫做深拷貝,我們必須顯式地定義拷貝構(gòu)造函數(shù)才能達(dá)到深拷貝的目的。深拷貝的例子比比皆是,除了上面的變長(zhǎng)數(shù)組類,使用的動(dòng)態(tài)數(shù)組類也需要深拷貝;此外,標(biāo)準(zhǔn)模板庫(STL)中的string、vector、stack、set、map等也都必須使用深拷貝。

讀者如果希望親眼目睹不使用深拷貝的后果,可以將上例中的拷貝構(gòu)造函數(shù)刪除,那么運(yùn)行結(jié)果將變?yōu)椋?,1,2,29,4,100,6,7,8,9

0,1,2,29,4,100,6,7,8,9

可以發(fā)現(xiàn),更改arr2的數(shù)據(jù)也影響到了arr1。這是因?yàn)?,在?chuàng)建arr2對(duì)象時(shí),默認(rèn)拷貝構(gòu)造函數(shù)將arr1.m_p直接賦值給了arr2.m_p,導(dǎo)致arr2.m_p和arr1.m_p指向了同一塊內(nèi)存,所以會(huì)相互影響。

另外需要注意的是,printArray()函數(shù)的形參為引用類型,這樣做能夠避免在傳參時(shí)調(diào)用拷貝構(gòu)造函數(shù);又因?yàn)閜rintArray()函數(shù)不會(huì)修改任何數(shù)組元素,所以我們添加了const限制,以使得語義更加明確。

到底是淺拷貝還是深拷貝

如果一個(gè)類擁有指針類型的成員變量,那么絕大部分情況下就需要深拷貝,因?yàn)橹挥羞@樣,才能將指針指向的內(nèi)容再復(fù)制出一份來,讓原有對(duì)象和新生對(duì)象相互獨(dú)立,彼此之間不受影響。如果類的成員變量沒有指針,一般淺拷貝足以。

另外一種需要深拷貝的情況就是在創(chuàng)建對(duì)象時(shí)進(jìn)行一些預(yù)處理工作,比如統(tǒng)計(jì)創(chuàng)建過的對(duì)象的數(shù)目、記錄對(duì)象創(chuàng)建的時(shí)間等,請(qǐng)看下面的例子:

#includeiostream

#includectime

#includewindows.h//在Linux和Mac下要換成unistd.h頭文件

usingnamespacestd;

classBase{

public:

Base(inta=0,intb=0);

Base(constBaseobj);//拷貝構(gòu)造函數(shù)

public:

intgetCount()const{returnm_count;}

time_tgetTime()const{returnm_time;}

private:

intm_a;

intm_b;

time_tm_time;//對(duì)象創(chuàng)建時(shí)間

staticintm_count;//創(chuàng)建過的對(duì)象的數(shù)目

intBase::m_count=0;

Base::Base(inta,intb):m_a(a),m_b(b){

m_count++;

m_time=time((time_t*)NULL);

Base::Base(constBaseobj){//拷貝構(gòu)造函數(shù)

this-m_a=obj.m_a;

this-m_b=obj.m_b;

this-m_count++;

this-m_time=time((time_t*)NULL);

intmain(){

Baseobj1(10,20);

cout"obj1:count="obj1.getCount()",time="obj1.getTime()endl;

Sleep(3000);//在Linux和Mac下要寫作sleep(3);

Baseobj2=obj1;

cout"obj2:count="obj2.getCount

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論