當(dāng)前位置:首頁 > 芯聞號 > 充電吧
[導(dǎo)讀]摘要:????在面向?qū)ο蟪绦蛟O(shè)計中,對象間的相互拷貝和賦值是經(jīng)常進行的操作。????如果對象在申明的同時馬上進行的初始化操作,則稱之為拷貝運算。例如:????????class1 A("af"); c


摘要:
????在面向?qū)ο蟪绦蛟O(shè)計中,對象間的相互拷貝和賦值是經(jīng)常進行的操作。
????如果對象在申明的同時馬上進行的初始化操作,則稱之為拷貝運算。例如:
????????class1 A("af"); class1?B=A;
?????此時其實際調(diào)用的是B(A)這樣的淺拷貝操作。
????如果對象在申明之后,在進行的賦值運算,我們稱之為賦值運算。例如:
????????class1 A("af"); class1 B;
????????B=A;
????????此時實際調(diào)用的類的缺省賦值函數(shù)B.operator=(A);
????????不管是淺拷貝還是賦值運算,其都有缺省的定義。也就是說,即使我們不overload這兩種operation,仍然可以運行。
那么,我們到底需不需要overload這兩種operation 呢?
????????答案就是:一般,我們我們需要手動編寫析構(gòu)函數(shù)的類,都需要overload 拷貝函數(shù)和賦值運算符。

????????
?下面介紹類的賦值運算符
1.C++中對象的內(nèi)存分配方式
????????在C++中,對象的實例在編譯的時候,就需要為其分配內(nèi)存大小,因此,系統(tǒng)都是在stack上為其分配內(nèi)存的。這一點和C#完全不同!千萬記?。涸贑#中,所有類都是reference type,要創(chuàng)建類的實體,必須通過new在heap上為其分配空間,同時返回在stack上指向其地址的reference.
????????因此,在C++中,只要申明該實例,在程序編譯后,就要為其分配相應(yīng)的內(nèi)存空間,至于實體內(nèi)的各個域的值,就由其構(gòu)造函數(shù)決定了。
????例如:
????class?A
{
public:
????A()
????{
????}
????A(int?id,char?*t_name)
????{
????_id=id;
????name=new?char[strlen(t_name)+1];
????strcpy(name,t_name);
????}
????private:
????????char *username;
????????int _id;
}

int?main()
{
A?a(1,"herengang");
A?b;
}
在程序編譯之后,a和b在stack上都被分配相應(yīng)的內(nèi)存大小。只不過對象a的域都被初始化,而b則都為隨機值。
其內(nèi)存分配如下:


2. 缺省情況下的賦值運算符
????如果我們執(zhí)行以下:
????b=a;
????????則其執(zhí)行的是缺省定義的缺省的賦值運算。所謂缺省的賦值運算,是指對象中的所有位于stack中的域,進行相應(yīng)的復(fù)制。但是,如果對象有位于heap上的域的話,其不會為拷貝對象分配heap上的空間,而只是指向相同的heap上的同一個地址。
????????執(zhí)行b=a這樣的缺省的賦值運算后,其內(nèi)存分配如下:

????????因此,對于缺省的賦值運算,如果對象域內(nèi)沒有heap上的空間,其不會產(chǎn)生任何問題。但是,如果對象域內(nèi)需要申請heap上的空間,那么在析構(gòu)對象的時候,就會連續(xù)兩次釋放heap上的同一塊內(nèi)存區(qū)域,從而導(dǎo)致異常。
????~A()
????{????????
????????delete?name;
????}
3.解決辦法--重載(overload)賦值運算符
????????因此,對于對象的域在heap上分配內(nèi)存的情況,我們必須重載賦值運算符。當(dāng)對象間進行拷貝的時候,我們必須讓不同對象的成員域指向其不同的heap地址--如果成員域?qū)儆趆eap的話。????
因此,重載賦值運算符后的代碼如下:class?A
{
public:

????A()
????{
????}
????A(int?id,char?*t_name)
????{
????????_id=id;
????????name=new?char[strlen(t_name)+1];
????????strcpy(name,t_name);
????}
????
????A&?operator?=(A&?a)
//注意:此處一定要返回對象的引用,否則返回后其值立即消失!
????{
????????????if(name!=NULL)
????????????????delete name;
????????this->_id=a._id;
????????int?len=strlen(a.name);
????????name=new?char[len+1];
????????strcpy(name,a.name);
????????return?*this;
????}

????~A()
????{
????????cout<<"~destructor"<<endl;
????????delete?name;
????}

????int?_id;
????char?*name;
};

int main()
{
?A a(1,"herengang");
?A b;
?b=a;
}

其內(nèi)存分配如下:

這樣,在對象a,b退出相應(yīng)的作用域,其調(diào)用相應(yīng)的析構(gòu)函數(shù),然后釋放分別屬于不同heap空間的內(nèi)存,程序正常結(jié)束。


references:
類的深拷貝函數(shù)的重載
????public class A
{
????public:
????????...
????????A(A &a);//重載拷貝函數(shù)
????????A& operator=(A &b);//重載賦值函數(shù)
????????//或者 我們也可以這樣重載賦值運算符?void operator=(A &a);即不返回任何值。如果這樣的話,他將不支持客戶代買中的鏈?zhǔn)劫x值 ,例如a=b=c will be prohibited!
????private:
????????int _id;
????????char *username;
}

A::A(A &a)
{
????_id=a._id;
????username=new char[strlen(a.username)+1];
????if(username!=NULL)
??????? strcpy(username,a.usernam);
}

A& A::operaton=(A &a)
{
????????if(this==&a)//? 問:什么需要判斷這個條件?(不是必須,只是優(yōu)化而已)。答案:提示:考慮a=a這樣的操作。
????????????return *this;
????????if(username!=NULL)
????????????delete username;
????????_id=a._id;
????????username=new char[strlen(a.username)+1];
????????if(username!=NULL)
??????????? strcpy(username,a.usernam);
????????return *this;????
}
//另外一種寫法:
void A::operation=(A &a)
{
????????if(username!=NULL)
????????????delete username;
????????_id=a._id;
????????username=new char[strlen(a.username)+1];
????????if(username!=NULL)
??????????? strcpy(username,a.usernam);
}

其實,從上可以看出,賦值運算符和拷貝函數(shù)很相似。只不過賦值函數(shù)最好有返回值(進行鏈?zhǔn)劫x值),返回也最好是對象的引用(為什么不是對象本身呢?note2有講解), 而拷貝函數(shù)不需要返回任何。同時,賦值函數(shù)首先要釋放掉對象自身的堆空間(如果需要的話),然后進行其他的operation.而拷貝函數(shù)不需要如此,因為對象此時還沒有分配堆空間。?

note1:
????不要按值向函數(shù)傳遞對象。如果對象有內(nèi)部指針指向動態(tài)分配的堆內(nèi)存,絲毫不要考慮把對象按值傳遞給函數(shù),要按引用傳遞。并記?。喝艉瘮?shù)不能改變參數(shù)對象的狀態(tài)和目標(biāo)對象的狀態(tài),則要使用const修飾符?

note2:問題:
????對于類的成員需要動態(tài)申請堆空間的類的對象,大家都知道,我們都最好要overload其賦值函數(shù)和拷貝函數(shù)??截悩?gòu)造函數(shù)是沒有任何返回類型的,這點毋庸置疑。 而賦值函數(shù)可以返回多種類型,例如以上講的void,類本身class1,以及類的引用 class &? 問,這幾種賦值函數(shù)的返回各有什么異同?
????答:1 如果賦值函數(shù)返回的是void ,我們知道,其唯一一點需要注意的是,其不支持鏈?zhǔn)劫x值運算,即a=b=c這樣是不允許的!
??????????2 對于返回的是類對象本身,還是類對象的引用,其有著本質(zhì)的區(qū)別!
??????????????第一:如果其返回的是類對象本身。
???A?operator?=(A&?a)
????{
????????????if(name!=NULL)
????????????????delete name;
????????this->_id=a._id;
????????int?len=strlen(a.name);
???????name=new?char[len+1];
????????strcpy(name,a.name);
????????return?*this;
????}
??????????其過程是這樣的:
???????????????????????class1 A("herengnag");
????????????????????????class1 B;???
????????????????????????B=A;
????????????????????看似簡單的賦值操作,其所有的過程如下:
???????????????????????1?釋放對象原來的堆資源
???????????????????????2?重新申請堆空間
???????????????????????3?拷貝源的值到對象的堆空間的值
???????????????????????4?創(chuàng)建臨時對象(調(diào)用臨時對象拷貝構(gòu)造函數(shù)),將臨時對象返回
???????????????????????5. 臨時對象結(jié)束,調(diào)用臨時對象析構(gòu)函數(shù),釋放臨時對象堆內(nèi)存
my god,還真復(fù)雜?。?br />????????????但是,在這些步驟里面,如果第4步,我們沒有overload 拷貝函數(shù),也就是沒有進行深拷貝。那么在進行第5步釋放臨時對象的heap 空間時,將釋放掉的是和目標(biāo)對象同一塊的heap空間。這樣當(dāng)目標(biāo)對象B作用域結(jié)束調(diào)用析構(gòu)函數(shù)時,就會產(chǎn)生錯誤??!
????????????因此,如果賦值運算符返回的是類對象本身,那么一定要overload 類的拷貝函數(shù)(進行深拷貝)!
????????????第二:如果賦值運算符返回的是對象的引用,
???A&?operator?=(A&?a)
????{
????????????if(name!=NULL)
????????????????delete name;
????????this->_id=a._id;
????????int?len=strlen(a.name);
???????name=new?char[len+1];
????????strcpy(name,a.name);
????????return?*this;
????}
????????那么其過程如下:
???????????????????1 釋放掉原來對象所占有的堆空間
???????????????????1.申請一塊新的堆內(nèi)存
???????????????????2 將源對象的堆內(nèi)存的值copy給新的堆內(nèi)存
???????????????????3 返回源對象的引用
????????????????????4 結(jié)束。
????因此,如果賦值運算符返回的是對象引用,那么其不會調(diào)用類的拷貝構(gòu)造函數(shù),這是問題的關(guān)鍵所在??!
?
完整代碼如下:

//?virtual.cpp?:?Defines?the?entry?point?for?the?console?application.
//

#include?"stdafx.h"
#include?"string.h"
#include?"stdlib.h"
#include?"assert.h"

class?complex
{
public:
????????int?real;
????????int?virt;
public:
????complex(){real=virt=0;}
????complex(int?treal,int?tvirt){real=treal;virt=tvirt;}
????complex?operator+(const?complex?&x)
????{
????????real+=x.real;
????????virt+=x.virt;
????????return?*this;
????}
????complex?operator=(const?complex?&x)
????{
????????return?complex(x.real,x.virt);
????}
};


class?A
{
public:
????A(){m_username=NULL;printf("null?constructor");}
????A(char?*username)
????{
????????int?len;
????????len=strlen(username);
????????m_username=new?char[len+1];//(char*)malloc(sizeof(len+1));
????????strcpy(m_username,username);
????????printf("nUsername?is?%sn",m_username);
????}
????
????A(A?&a);
????A?operator=(A?&b);
????int?test(const?int?&x)
????{
????????return?x;
????}

????virtual?~A()
????{
????//????if(m_username)
????????{
????????delete?m_username;
????????printf("nA?is?destructedn");
????????}
????}



protected:
????char?*m_username;

};



A::A(A?&a)
{

????int?len=strlen(a.m_username);
????this->m_username=new?char[len+2];
????strcpy(m_username,a.m_username);
????strcat(m_username,"f");
????printf("ndeep?copy?function");
}


A?A::operator=(A?&b)
{
????if(m_username)
????????delete?m_username;

????int?len=strlen(b.m_username);
????this->m_username=new?char[len+1];
????strcpy(m_username,b.m_username);
//????printf("copied?successfully!");
?????return?*this;
}

?

class?B:public?A
{
public:
????B(char?*username,char?*password):A(username)
????{
????????int?len=strlen(password)+1;
????????m_password=new?char[len];//(char?*)malloc(sizeof(len));
????????strcpy(m_password,password);
????????printf("username:%s,password:%sn",m_username,m_password);
????}
????~B()
????{
????????delete?m_password;
????????printf("B?is?destructedn");
????}
protected:
????char?*m_password;
};

int?main(int?argc,?char*?argv[])
{
//????B?b("herengang","982135");
//????A?*a=&b;
//????delete?a;
????A?a("haha");
????A?b;

????printf("nbegin?to?invoke?copy?function");
????b=a;

//????printf("%d",b.test(2));
????//complex?x(1,3),y(1,4);
????//x=(x+y);
????//printf("%d,%d",x.real,x.virt);
????return?0;


本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉