欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++直接初始化與復(fù)制初始化的區(qū)別深入解析

 更新時(shí)間:2014年09月04日 12:09:38   投稿:shichen2014  
這篇文章主要介紹了C++直接初始化與復(fù)制初始化的區(qū)別深入解析,是很多C++初學(xué)者需要深入了解的重要概念,需要的朋友可以參考下

C++中直接初始化與復(fù)制初始化是很多初學(xué)者容易混淆的概念,本文就以實(shí)例形式講述二者之間的區(qū)別。供大家參考之用。具體分析如下:

一、Primer中的說(shuō)法

首先我們現(xiàn)來(lái)看看經(jīng)典是怎么說(shuō)的:

“當(dāng)用于類類型對(duì)象時(shí),初始化的復(fù)制形式和直接形式有所不同:直接初始化直接調(diào)用與實(shí)參匹配的構(gòu)造函數(shù),復(fù)制初始化總是調(diào)用復(fù)制構(gòu)造函數(shù)。復(fù)制初始化首先使用指定構(gòu)造函數(shù)創(chuàng)建一個(gè)臨時(shí)對(duì)象,然后用復(fù)制構(gòu)造函數(shù)將那個(gè)臨時(shí)對(duì)象復(fù)制到正在創(chuàng)建的對(duì)象”

還有一段這樣說(shuō):

通常直接初始化和復(fù)制初始化僅在低級(jí)別優(yōu)化上存在差異,然而,對(duì)于不支持復(fù)制的類型,或者使用非explicit構(gòu)造函數(shù)的時(shí)候,它們有本質(zhì)區(qū)別

ifstream file1("filename")://ok:direct initialization
ifstream file2 = "filename";//error:copy constructor is private”

二、通常的誤解

從上面的說(shuō)法中,我們可以知道,直接初始化不一定要調(diào)用復(fù)制構(gòu)造函數(shù),而復(fù)制初始化一定要調(diào)用復(fù)制構(gòu)造函數(shù)。然而大多數(shù)人卻認(rèn)為,直接初始化是構(gòu)造對(duì)象時(shí)要調(diào)用復(fù)制構(gòu)造函數(shù),而復(fù)制初始化是構(gòu)造對(duì)象時(shí)要調(diào)用賦值操作函數(shù)(operator=),其實(shí)這是一大誤解。因?yàn)橹挥袑?duì)象被創(chuàng)建才會(huì)出現(xiàn)初始化,而賦值操作并不應(yīng)用于對(duì)象的創(chuàng)建過(guò)程中,且primer也沒(méi)有這樣的說(shuō)法。至于為什么會(huì)出現(xiàn)這個(gè)誤解,可能是因?yàn)閺?fù)制初始化的寫法中存在等號(hào)(=)吧。

為了把問(wèn)題說(shuō)清楚,還是從代碼上來(lái)解釋比較容易讓人明白,請(qǐng)看下面的代碼:

#include <iostream> 
#include <cstring> 
using namespace std; 
 
class ClassTest 
{ 
public: 
ClassTest() 
{ 
c[0] = '\0'; 
cout<<"ClassTest()"<<endl; 
} 
ClassTest& operator=(const ClassTest &ct) 
{ 
strcpy(c, ct.c); 
cout<<"ClassTest& operator=(const ClassTest &ct)"<<endl; 
return *this; 
} 
ClassTest(const char *pc) 
{ 
strcpy(c, pc); 
cout<<"ClassTest (const char *pc)"<<endl; 
} 
// private: 
ClassTest(const ClassTest& ct) 
{ 
strcpy(c, ct.c); 
cout<<"ClassTest(const ClassTest& ct)"<<endl; 
} 
private: 
char c[256]; 
}; 
 
int main() 
{ 
cout<<"ct1: "; 
ClassTest ct1("ab");//直接初始化 
cout<<"ct2: "; 
ClassTest ct2 = "ab";//復(fù)制初始化 
cout<<"ct3: "; 
ClassTest ct3 = ct1;//復(fù)制初始化 
cout<<"ct4: "; 
ClassTest ct4(ct1);//直接初始化 
cout<<"ct5: "; 
ClassTest ct5 = ClassTest();//復(fù)制初始化 
return 0; 
} 

輸出結(jié)果為:

從輸出的結(jié)果,我們可以知道對(duì)象的構(gòu)造到底調(diào)用了哪些函數(shù),從ct1與ct2、ct3與ct4的比較中可以看出,ct1與ct2對(duì)象的構(gòu)建調(diào)用的都是同一個(gè)函數(shù)——ClassTest(const char *pc),同樣道理,ct3與ct4調(diào)用的也是同一個(gè)函數(shù)——ClassTest(const ClassTest& ct),而ct5則直接調(diào)用了默認(rèn)構(gòu)造函數(shù)。

于是,很多人就認(rèn)為ClassTest ct1("ab");等價(jià)于ClassTest ct2 = "ab";,而ClassTest ct3 = ct1;也等價(jià)于ClassTest ct4(ct1);而且他們都沒(méi)有調(diào)用賦值操作函數(shù),所以它們都是直接初始化,然而事實(shí)是否真的如你所想的那樣呢?答案顯然不是。

三、層層推進(jìn),到底誰(shuí)欺騙了我們

很多時(shí)候,自己的眼睛往往會(huì)欺騙你自己,這里就是一個(gè)例子,正是你的眼睛欺騙了你。為什么會(huì)這樣?其中的原因在談優(yōu)化時(shí)的補(bǔ)充中也有說(shuō)明,就是因?yàn)榫幾g會(huì)幫你做很多你看不到,你也不知道的優(yōu)化,你看到的結(jié)果,正是編譯器做了優(yōu)化后的代碼的運(yùn)行結(jié)果,并不是你的代碼的真正運(yùn)行結(jié)果。

你也許不相信我所說(shuō)的,那么你可以把類中的復(fù)制函數(shù)函數(shù)中面注釋起來(lái)的那行取消注釋,讓復(fù)制構(gòu)造函數(shù)成為私有函數(shù)再編譯運(yùn)行這個(gè)程序,看看有什么結(jié)果發(fā)生。

很明顯,發(fā)生了編譯錯(cuò)誤,從上面的運(yùn)行結(jié)果,你可能會(huì)認(rèn)為是因?yàn)閏t3和ct4在構(gòu)建過(guò)程中用到了復(fù)制構(gòu)造函數(shù)——ClassTest(const ClassTest& ct),而現(xiàn)在它變成了私有函數(shù),不能在類的外面使用,所以出現(xiàn)了編譯錯(cuò)誤,但是你也可以把ct3和ct4的函數(shù)語(yǔ)句注釋起來(lái),如下所示:

int main() 
{ 
cout<<"ct1: "; 
ClassTest ct1("ab"); 
cout<<"ct2: "; 
ClassTest ct2 = "ab"; 
// cout<<"ct3: "; 
// ClassTest ct3 = ct1; 
// cout<<"ct4: "; 
// ClassTest ct4(ct1); 
cout<<"ct5: "; 
ClassTest ct5 = ClassTest(); 
return 0; 
} 

然而你還是非常遺憾地發(fā)現(xiàn),還是沒(méi)有編譯通過(guò)。這是為什么呢?從上面的語(yǔ)句和之前的運(yùn)行結(jié)果來(lái)看,的確是已經(jīng)沒(méi)有調(diào)用復(fù)制構(gòu)造函數(shù)了,為什么還是編譯錯(cuò)誤呢?

經(jīng)過(guò)實(shí)驗(yàn),main函數(shù)只有這樣才能通過(guò)編譯:

int main() 
{ 
cout<<"ct1: "; 
ClassTest ct1("ab"); 
return 0; 
} 

在這里我們可以看到,原來(lái)是復(fù)制構(gòu)造函數(shù)欺騙了我們。

四、揭開真相

看到這里,你可能已經(jīng)大驚失色,下面就讓我來(lái)揭開這個(gè)真相吧!

還是那一句,什么是直接初始化,而什么又是復(fù)制初始化呢?

簡(jiǎn)單點(diǎn)來(lái)說(shuō),就是定義對(duì)象時(shí)的寫法不一樣,一個(gè)用括號(hào),如ClassTest ct1("ab"),而一個(gè)用等號(hào),如ClassTest ct2 = "ab"。

但是從本質(zhì)來(lái)說(shuō),它們卻有本質(zhì)的不同:直接初始化直接調(diào)用與實(shí)參匹配的構(gòu)造函數(shù),復(fù)制初始化總是調(diào)用復(fù)制構(gòu)造函數(shù)。復(fù)制初始化首先使用指定構(gòu)造函數(shù)創(chuàng)建一個(gè)臨時(shí)對(duì)象,然后用復(fù)制構(gòu)造函數(shù)將那個(gè)臨時(shí)對(duì)象復(fù)制到正在創(chuàng)建的對(duì)象。所以當(dāng)復(fù)制構(gòu)造函數(shù)被聲明為私有時(shí),所有的復(fù)制初始化都不能使用。

現(xiàn)在我們?cè)賮?lái)看回main函數(shù)中的語(yǔ)句:

1、ClassTest ct1("ab");這條語(yǔ)句屬于直接初始化,它不需要調(diào)用復(fù)制構(gòu)造函數(shù),直接調(diào)用構(gòu)造函數(shù)ClassTest(const char *pc),所以當(dāng)復(fù)制構(gòu)造函數(shù)變?yōu)樗接袝r(shí),它還是能直接執(zhí)行的。

2、ClassTest ct2 = "ab";這條語(yǔ)句為復(fù)制初始化,它首先調(diào)用構(gòu)造函數(shù)ClassTest(const char *pc)函數(shù)創(chuàng)建一個(gè)臨時(shí)對(duì)象,然后調(diào)用復(fù)制構(gòu)造函數(shù),把這個(gè)臨時(shí)對(duì)象作為參數(shù),構(gòu)造對(duì)象ct2;所以當(dāng)復(fù)制構(gòu)造函數(shù)變?yōu)樗接袝r(shí),該語(yǔ)句不能編譯通過(guò)。

3、ClassTest ct3 = ct1;這條語(yǔ)句為復(fù)制初始化,因?yàn)閏t1本來(lái)已經(jīng)存在,所以不需要調(diào)用相關(guān)的構(gòu)造函數(shù),而直接調(diào)用復(fù)制構(gòu)造函數(shù),把它值復(fù)制給對(duì)象ct3;所以當(dāng)復(fù)制構(gòu)造函數(shù)變?yōu)樗接袝r(shí),該語(yǔ)句不能編譯通過(guò)。

4、ClassTest ct4(ct1);這條語(yǔ)句為直接初始化,因?yàn)閏t1本來(lái)已經(jīng)存在,直接調(diào)用復(fù)制構(gòu)造函數(shù),生成對(duì)象ct3的副本對(duì)象ct4。所以當(dāng)復(fù)制構(gòu)造函數(shù)變?yōu)樗接袝r(shí),該語(yǔ)句不能編譯通過(guò)。

注:第4個(gè)對(duì)象ct4與第3個(gè)對(duì)象ct3的創(chuàng)建所調(diào)用的函數(shù)是一樣的,但是本人卻認(rèn)為,調(diào)用復(fù)制函數(shù)的原因卻有所不同。因?yàn)橹苯映跏蓟歉鶕?jù)參數(shù)來(lái)調(diào)用構(gòu)造函數(shù)的,如ClassTest ct4(ct1),它是根據(jù)括號(hào)中的參數(shù)(一個(gè)本類的對(duì)象),來(lái)直接確定為調(diào)用復(fù)制構(gòu)造函數(shù)ClassTest(const ClassTest& ct),這跟函數(shù)重載時(shí),會(huì)根據(jù)函數(shù)調(diào)用時(shí)的參數(shù)來(lái)調(diào)用相應(yīng)的函數(shù)是一個(gè)道理;而對(duì)于ct3則不同,它的調(diào)用并不是像ct4時(shí)那樣,是根據(jù)參數(shù)來(lái)確定要調(diào)用復(fù)制構(gòu)造函數(shù)的,它只是因?yàn)槌跏蓟厝灰{(diào)用復(fù)制構(gòu)造函數(shù)而已。它理應(yīng)要?jiǎng)?chuàng)建一個(gè)臨時(shí)對(duì)象,但只是這個(gè)對(duì)象卻已經(jīng)存在,所以就省去了這一步,然后直接調(diào)用復(fù)制構(gòu)造函數(shù),因?yàn)閺?fù)制初始化必然要調(diào)用復(fù)制構(gòu)造函數(shù),所以ct3的創(chuàng)建仍是復(fù)制初始化。

5、ClassTest ct5 = ClassTest();這條語(yǔ)句為復(fù)制初始化,首先調(diào)用默認(rèn)構(gòu)造函數(shù)產(chǎn)生一個(gè)臨時(shí)對(duì)象,然后調(diào)用復(fù)制構(gòu)造函數(shù),把這個(gè)臨時(shí)對(duì)象作為參數(shù),構(gòu)造對(duì)象ct5。所以當(dāng)復(fù)制構(gòu)造函數(shù)變?yōu)樗接袝r(shí),該語(yǔ)句不能編譯通過(guò)。

五、假象產(chǎn)生的原因

產(chǎn)生上面的運(yùn)行結(jié)果的主要原因在于編譯器的優(yōu)化,而為什么把復(fù)制構(gòu)造函數(shù)聲明為私有(private)就能把這個(gè)假象去掉呢?主要是因?yàn)閺?fù)制構(gòu)造函數(shù)是可以由編譯默認(rèn)合成的,而且是公有的(public),編譯器就是根據(jù)這個(gè)特性來(lái)對(duì)代碼進(jìn)行優(yōu)化的。然而如里你自己定義這個(gè)復(fù)制構(gòu)造函數(shù),編譯則不會(huì)自動(dòng)生成,雖然編譯不會(huì)自動(dòng)生成,但是如果你自己定義的復(fù)制構(gòu)造函數(shù)仍是公有的話,編譯還是會(huì)為你做同樣的優(yōu)化。然而當(dāng)它是私有成員時(shí),編譯器就會(huì)有很不同的舉動(dòng),因?yàn)槟忝鞔_地告訴了編譯器,你明確地拒絕了對(duì)象之間的復(fù)制操作,所以它也就不會(huì)幫你做之前所做的優(yōu)化,你的代碼的本來(lái)面目就出來(lái)了。

舉個(gè)例子來(lái)說(shuō),就像下面的語(yǔ)句:

ClassTest ct2 = "ab";

它本來(lái)是要這樣來(lái)構(gòu)造對(duì)象的:首先調(diào)用構(gòu)造函數(shù)ClassTest(const char *pc)函數(shù)創(chuàng)建一個(gè)臨時(shí)對(duì)象,然后調(diào)用復(fù)制構(gòu)造函數(shù),把這個(gè)臨時(shí)對(duì)象作為參數(shù),構(gòu)造對(duì)象ct2。然而編譯也發(fā)現(xiàn),復(fù)制構(gòu)造函數(shù)是公有的,即你明確地告訴了編譯器,你允許對(duì)象之間的復(fù)制,而且此時(shí)它發(fā)現(xiàn)可以通過(guò)直接調(diào)用重載的構(gòu)造函數(shù)ClassTest(const char *pc)來(lái)直接初始化對(duì)象,而達(dá)到相同的效果,所以就把這條語(yǔ)句優(yōu)化為ClassTest ct2("ab")。

而如果把復(fù)制構(gòu)造函數(shù)聲明為私有的,則對(duì)象之前的復(fù)制不能進(jìn)行,即不能把臨時(shí)對(duì)像作為參數(shù),調(diào)用復(fù)制構(gòu)造函數(shù),所以編譯就認(rèn)為ClassTest ct2 = "ab"與ClassTest ct2("ab")是不等價(jià)的,也就不會(huì)幫你做這個(gè)優(yōu)化,所以編譯出錯(cuò)了。

注:根據(jù)上面的代碼,有些人可能會(huì)運(yùn)行出與本人測(cè)試不一樣的結(jié)果,這是為什么呢?就像前面所說(shuō)的那樣,編譯器會(huì)為代碼做一定的優(yōu)化,但是不同的編譯器所作的優(yōu)化的方案卻可能有所不同,所以當(dāng)你使用不同的編譯器時(shí),由于這些優(yōu)化的方案不一樣,可能會(huì)產(chǎn)生不同的結(jié)果,我這里用的是g++4.7。

相信本文所述對(duì)大家深入學(xué)習(xí)C++程序設(shè)計(jì)有一定的參考借鑒作用。

相關(guān)文章

  • C++二維數(shù)組螺旋加密信息

    C++二維數(shù)組螺旋加密信息

    大家好,本篇文章主要講的是C++二維數(shù)組螺旋加密信息,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • 深入c++中臨時(shí)對(duì)象的析構(gòu)時(shí)機(jī)的詳解

    深入c++中臨時(shí)對(duì)象的析構(gòu)時(shí)機(jī)的詳解

    本篇文章對(duì)c++中臨時(shí)對(duì)象的析構(gòu)時(shí)機(jī)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 關(guān)于C++友元函數(shù)的實(shí)現(xiàn)講解

    關(guān)于C++友元函數(shù)的實(shí)現(xiàn)講解

    今天小編就為大家分享一篇關(guān)于關(guān)于C++友元函數(shù)的實(shí)現(xiàn)講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • 適合新手小白DEV?C++的使用方法

    適合新手小白DEV?C++的使用方法

    Dev-C++是一個(gè)Windows環(huán)境下C/C++的集成開發(fā)環(huán)境(IDE),它是一款自由軟件,遵守GPL,下面這篇文章主要給大家介紹了關(guān)于適合新手小白DEV?C++的使用方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • C語(yǔ)言中宏定義使用的小細(xì)節(jié)

    C語(yǔ)言中宏定義使用的小細(xì)節(jié)

    本篇文章是對(duì)C語(yǔ)言中宏定義使用的小細(xì)節(jié)進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • Cocos2d-x UI開發(fā)之CCControlSwitch控件類使用實(shí)例

    Cocos2d-x UI開發(fā)之CCControlSwitch控件類使用實(shí)例

    這篇文章主要介紹了Cocos2d-x UI開發(fā)之CCControlSwitch控件類使用實(shí)例,本文代碼中含大量注釋講解了CCControlSwitch控件類的使用,需要的朋友可以參考下
    2014-09-09
  • C++實(shí)現(xiàn)LeetCode(66.加一運(yùn)算)

    C++實(shí)現(xiàn)LeetCode(66.加一運(yùn)算)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(66.加一運(yùn)算),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • 詳解QListWidget如何實(shí)現(xiàn)自定義Item效果

    詳解QListWidget如何實(shí)現(xiàn)自定義Item效果

    這篇文章主要為大家介紹了如何通過(guò)QListWidget實(shí)現(xiàn)自定義Item效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2022-04-04
  • C++標(biāo)準(zhǔn)模板庫(kù)string類的介紹與使用講解

    C++標(biāo)準(zhǔn)模板庫(kù)string類的介紹與使用講解

    今天小編就為大家分享一篇關(guān)于C++標(biāo)準(zhǔn)模板庫(kù)string類的介紹與使用講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-12-12
  • C語(yǔ)言詳細(xì)分析講解關(guān)鍵字goto與void的作用

    C語(yǔ)言詳細(xì)分析講解關(guān)鍵字goto與void的作用

    我們?cè)贑語(yǔ)言中經(jīng)常會(huì)見到void,也會(huì)偶爾見到goto,那么C語(yǔ)言中既然有g(shù)oto,為什么我們?cè)诖a中見的很少呢?在以前很多的項(xiàng)目經(jīng)驗(yàn)中,我們得到這樣一條潛規(guī)則:一般項(xiàng)目都是禁用goto的,程序質(zhì)量與goto的出現(xiàn)次數(shù)成反比。自后也就造成了我們一般不會(huì)使用goto
    2022-04-04

最新評(píng)論