溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

詳解C++中構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)的區(qū)別和實(shí)現(xiàn)

發(fā)布時(shí)間:2020-10-13 17:05:18 來(lái)源:腳本之家 閱讀:137 作者:Zmyths 欄目:編程語(yǔ)言

C++中一般創(chuàng)建對(duì)象,拷貝或賦值的方式有構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù)這三種方法。下面就詳細(xì)比較下三者之間的區(qū)別以及它們的具體實(shí)現(xiàn)

1.構(gòu)造函數(shù)

構(gòu)造函數(shù)是一種特殊的類成員函數(shù),是當(dāng)創(chuàng)建一個(gè)類的對(duì)象時(shí),它被調(diào)用來(lái)對(duì)類的數(shù)據(jù)成員進(jìn)行初始化和分配內(nèi)存。(構(gòu)造函數(shù)的命名必須和類名完全相同)

首先說(shuō)一下一個(gè)C++的空類,編譯器會(huì)加入哪些默認(rèn)的成員函數(shù)

默認(rèn)構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)

析構(gòu)函數(shù)

賦值函數(shù)(賦值運(yùn)算符)

取值函數(shù)

**即使程序沒(méi)定義任何成員,編譯器也會(huì)插入以上的函數(shù)!

注意:構(gòu)造函數(shù)可以被重載,可以多個(gè),可以帶參數(shù);析構(gòu)函數(shù)只有一個(gè),不能被重載,不帶參數(shù)

而默認(rèn)構(gòu)造函數(shù)沒(méi)有參數(shù),它什么也不做。當(dāng)沒(méi)有重載無(wú)參構(gòu)造函數(shù)時(shí),

A a就是通過(guò)默認(rèn)構(gòu)造函數(shù)來(lái)創(chuàng)建一個(gè)對(duì)象

下面代碼為構(gòu)造函數(shù)重載的實(shí)現(xiàn)

<span >class A
{
int m_i;
Public:
 A() 
{
 Cout<<”無(wú)參構(gòu)造函數(shù)”<<endl;
}
A(int i):m_i(i) {} //初始化列表
}</span>

2.拷貝構(gòu)造函數(shù)

拷貝構(gòu)造函數(shù)是C++獨(dú)有的,它是一種特殊的構(gòu)造函數(shù),用基于同一類的一個(gè)對(duì)象構(gòu)造和初始化另一個(gè)對(duì)象。

當(dāng)沒(méi)有重載拷貝構(gòu)造函數(shù)時(shí),通過(guò)默認(rèn)拷貝構(gòu)造函數(shù)來(lái)創(chuàng)建一個(gè)對(duì)象

A a;

A b(a);

A b=a;  都是拷貝構(gòu)造函數(shù)來(lái)創(chuàng)建對(duì)象b

強(qiáng)調(diào):這里b對(duì)象是不存在的,是用a 對(duì)象來(lái)構(gòu)造和初始化b的??!

先說(shuō)下什么時(shí)候拷貝構(gòu)造函數(shù)會(huì)被調(diào)用:

在C++中,3種對(duì)象需要復(fù)制,此時(shí)拷貝構(gòu)造函數(shù)會(huì)被調(diào)用

  1. 1)一個(gè)對(duì)象以值傳遞的方式傳入函數(shù)體
  2. 2)一個(gè)對(duì)象以值傳遞的方式從函數(shù)返回
  3. 3)一個(gè)對(duì)象需要通過(guò)另一個(gè)對(duì)象進(jìn)行初始化

什么時(shí)候編譯器會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù):

  1. 1)如果用戶沒(méi)有自定義拷貝構(gòu)造函數(shù),并且在代碼中使用到了拷貝構(gòu)造函數(shù),編譯器就會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。但如果用戶定義了拷貝構(gòu)造函數(shù),編譯器就不在生成。
  2. 2)如果用戶定義了一個(gè)構(gòu)造函數(shù),但不是拷貝構(gòu)造函數(shù),而此時(shí)代碼中又用到了拷貝構(gòu)造函數(shù),那編譯器也會(huì)生成默認(rèn)的拷貝構(gòu)造函數(shù)。

因?yàn)橄到y(tǒng)提供的默認(rèn)拷貝構(gòu)造函數(shù)工作方式是內(nèi)存拷貝,也就是淺拷貝。如果對(duì)象中用到了需要手動(dòng)釋放的對(duì)象,則會(huì)出現(xiàn)問(wèn)題,這時(shí)就要手動(dòng)重載拷貝構(gòu)造函數(shù),實(shí)現(xiàn)深拷貝。

下面說(shuō)說(shuō)深拷貝與淺拷貝:

  1. 淺拷貝:如果復(fù)制的對(duì)象中引用了一個(gè)外部?jī)?nèi)容(例如分配在堆上的數(shù)據(jù)),那么在復(fù)制這個(gè)對(duì)象的時(shí)候,讓新舊兩個(gè)對(duì)象指向同一個(gè)外部?jī)?nèi)容,就是淺拷貝。(指針雖然復(fù)制了,但所指向的空間內(nèi)容并沒(méi)有復(fù)制,而是由兩個(gè)對(duì)象共用,兩個(gè)對(duì)象不獨(dú)立,刪除空間存在)
  2. 深拷貝:如果在復(fù)制這個(gè)對(duì)象的時(shí)候?yàn)樾聦?duì)象制作了外部對(duì)象的獨(dú)立復(fù)制,就是深拷貝。

拷貝構(gòu)造函數(shù)重載聲明如下:

A (const A&other)

下面為拷貝構(gòu)造函數(shù)的實(shí)現(xiàn):

<span >class A
{
 int m_i
 A(const A& other):m_i(other.m_i)
{
 Cout<<”拷貝構(gòu)造函數(shù)”<<endl;
}
}</span>

3.賦值函數(shù)

當(dāng)一個(gè)類的對(duì)象向該類的另一個(gè)對(duì)象賦值時(shí),就會(huì)用到該類的賦值函數(shù)。

當(dāng)沒(méi)有重載賦值函數(shù)(賦值運(yùn)算符)時(shí),通過(guò)默認(rèn)賦值函數(shù)來(lái)進(jìn)行賦值操作

A a;

A b;

b=a; 

強(qiáng)調(diào):這里a,b對(duì)象是已經(jīng)存在的,是用a 對(duì)象來(lái)賦值給b的??!

賦值運(yùn)算的重載聲明如下:

 A& operator = (const A& other)

通常大家會(huì)對(duì)拷貝構(gòu)造函數(shù)和賦值函數(shù)混淆,這兒仔細(xì)比較兩者的區(qū)別:

1)拷貝構(gòu)造函數(shù)是一個(gè)對(duì)象初始化一塊內(nèi)存區(qū)域,這塊內(nèi)存就是新對(duì)象的內(nèi)存區(qū),而賦值函數(shù)是對(duì)于一個(gè)已經(jīng)被初始化的對(duì)象來(lái)進(jìn)行賦值操作。

<span >class A;
A a;
A b=a;  //調(diào)用拷貝構(gòu)造函數(shù)(b不存在)
A c(a) ;  //調(diào)用拷貝構(gòu)造函數(shù)
 
/****/
 
class A;
A a;
A b;  
b = a ;  //調(diào)用賦值函數(shù)(b存在)</span>

2)一般來(lái)說(shuō)在數(shù)據(jù)成員包含指針對(duì)象的時(shí)候,需要考慮兩種不同的處理需求:一種是復(fù)制指針對(duì)象,另一種是引用指針對(duì)象??截悩?gòu)造函數(shù)大多數(shù)情況下是復(fù)制,而賦值函數(shù)是引用對(duì)象

3)實(shí)現(xiàn)不一樣??截悩?gòu)造函數(shù)首先是一個(gè)構(gòu)造函數(shù),它調(diào)用時(shí)候是通過(guò)參數(shù)的對(duì)象初始化產(chǎn)生一個(gè)對(duì)象。賦值函數(shù)則是把一個(gè)新的對(duì)象賦值給一個(gè)原有的對(duì)象,所以如果原來(lái)的對(duì)象中有內(nèi)存分配要先把內(nèi)存釋放掉,而且還要檢察一下兩個(gè)對(duì)象是不是同一個(gè)對(duì)象,如果是,不做任何操作,直接返回。(這些要點(diǎn)會(huì)在下面的String實(shí)現(xiàn)代碼中體現(xiàn))

?。?!如果不想寫(xiě)拷貝構(gòu)造函數(shù)和賦值函數(shù),又不允許別人使用編譯器生成的缺省函數(shù),最簡(jiǎn)單的辦法是將拷貝構(gòu)造函數(shù)和賦值函數(shù)聲明為私有函數(shù),不用編寫(xiě)代碼。如:

<span >class A
{
 private:
 A(const A& a); //私有拷貝構(gòu)造函數(shù)
 A& operate=(const A& a); //私有賦值函數(shù)
}</span>

如果程序這樣寫(xiě)就會(huì)出錯(cuò):

<span >A a;
A b(a); //調(diào)用了私有拷貝構(gòu)造函數(shù),編譯出錯(cuò)
 
A b;
b=a; //調(diào)用了私有賦值函數(shù),編譯出錯(cuò)</span>

所以如果類定義中有指針或引用變量或?qū)ο?,為了避免潛在錯(cuò)誤,最好重載拷貝構(gòu)造函數(shù)和賦值函數(shù)。

下面以string類的實(shí)現(xiàn)為例,完整的寫(xiě)了普通構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù)的實(shí)現(xiàn)。String類的基本實(shí)現(xiàn)見(jiàn)我另一篇博文。

<span >String::String(const char* str)  //普通構(gòu)造函數(shù)
 
{
 
 cout<<construct<<endl;
 
 if(str==NULL)    //如果str 為NULL,就存一個(gè)空字符串“”
 
{
 m_string=new char[1];
 *m_string ='\0';
}
 
 else
 
{
 
 m_string= new char[strlen(str)+1] ;  //分配空間
 strcpy(m_string,str);
 
}
 
}
 
 
String::String(const String&other)  //拷貝構(gòu)造函數(shù)
 
{
 cout<<"copy construct"<<endl;
 m_string=new char[strlen(other.m_string)+1]; //分配空間并拷貝
 strcpy(m_string,other.m_string);
}
 
String & String::operator=(const String& other) //賦值運(yùn)算符
{
 cout<<"operator =funtion"<<endl ;
 if(this==&other) //如果對(duì)象和other是用一個(gè)對(duì)象,直接返回本身
 {
 return *this;
 }
 delete []m_string; //先釋放原來(lái)的內(nèi)存
 m_string= new char[strlen(other.m_string)+1];
 strcpy(m_string,other.m_string);
 return * this;
}</span>

一句話記住三者:

對(duì)象不存在,且沒(méi)用別的對(duì)象來(lái)初始化,就是調(diào)用了構(gòu)造函數(shù);

對(duì)象不存在,且用別的對(duì)象來(lái)初始化,就是拷貝構(gòu)造函數(shù)(上面說(shuō)了三種用它的情況?。?/p>

對(duì)象存在,用別的對(duì)象來(lái)給它賦值,就是賦值函數(shù)。

以上為本人結(jié)合很多資料和圖書(shū)整理出來(lái)的,將核心的點(diǎn)都系統(tǒng)的理出來(lái),全自己按條理寫(xiě)的,現(xiàn)在大家對(duì)普通構(gòu)造函數(shù),拷貝構(gòu)造函數(shù),賦值函數(shù)的區(qū)別和實(shí)現(xiàn)應(yīng)該都清楚了。

以上所述是小編給大家介紹的C++中構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)和賦值函數(shù)的區(qū)別和實(shí)現(xiàn)詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)億速云網(wǎng)站的支持!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI