最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

一、Primer中的說法

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

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

還有一段這樣說:

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

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

二、通常的誤解

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

為了把問題說清楚,還是從代碼上來解釋比較容易讓人明白,請看下面的代碼:

#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";//復制初始化 
cout<<"ct3: "; 
ClassTest ct3 = ct1;//復制初始化 
cout<<"ct4: "; 
ClassTest ct4(ct1);//直接初始化 
cout<<"ct5: "; 
ClassTest ct5 = ClassTest();//復制初始化 
return 0; 
} 

輸出結(jié)果為:

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

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

三、層層推進,到底誰欺騙了我們

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

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

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

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),還是沒有編譯通過。這是為什么呢?從上面的語句和之前的運行結(jié)果來看,的確是已經(jīng)沒有調(diào)用復制構(gòu)造函數(shù)了,為什么還是編譯錯誤呢?

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

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

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

四、揭開真相

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

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

簡單點來說,就是定義對象時的寫法不一樣,一個用括號,如ClassTest ct1("ab"),而一個用等號,如ClassTest ct2 = "ab"。

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

現(xiàn)在我們再來看回main函數(shù)中的語句:

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

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

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

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

注:第4個對象ct4與第3個對象ct3的創(chuàng)建所調(diào)用的函數(shù)是一樣的,但是本人卻認為,調(diào)用復制函數(shù)的原因卻有所不同。因為直接初始化是根據(jù)參數(shù)來調(diào)用構(gòu)造函數(shù)的,如ClassTest ct4(ct1),它是根據(jù)括號中的參數(shù)(一個本類的對象),來直接確定為調(diào)用復制構(gòu)造函數(shù)ClassTest(const ClassTest& ct),這跟函數(shù)重載時,會根據(jù)函數(shù)調(diào)用時的參數(shù)來調(diào)用相應的函數(shù)是一個道理;而對于ct3則不同,它的調(diào)用并不是像ct4時那樣,是根據(jù)參數(shù)來確定要調(diào)用復制構(gòu)造函數(shù)的,它只是因為初始化必然要調(diào)用復制構(gòu)造函數(shù)而已。它理應要創(chuàng)建一個臨時對象,但只是這個對象卻已經(jīng)存在,所以就省去了這一步,然后直接調(diào)用復制構(gòu)造函數(shù),因為復制初始化必然要調(diào)用復制構(gòu)造函數(shù),所以ct3的創(chuàng)建仍是復制初始化。

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

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

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

舉個例子來說,就像下面的語句:

ClassTest ct2 = "ab";

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

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

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

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

相關(guān)文章

  • C++使用string的大數(shù)快速模冪運算(6)

    C++使用string的大數(shù)快速模冪運算(6)

    這篇文章主要為大家詳細介紹了C++使用string的大數(shù)快速模冪運算,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • C++類與對象深入之靜態(tài)成員與友元及內(nèi)部類詳解

    C++類與對象深入之靜態(tài)成員與友元及內(nèi)部類詳解

    朋友們好,這篇播客我們繼續(xù)C++的初階學習,現(xiàn)在對我們對C++的靜態(tài)成員,友元,內(nèi)部類知識點做出總結(jié),整理出來一篇博客供我們一起復習和學習,如果文章中有理解不當?shù)牡胤?還希望朋友們在評論區(qū)指出,我們相互學習,共同進步
    2022-06-06
  • c++截取漢字和英文混合字符串代碼實例

    c++截取漢字和英文混合字符串代碼實例

    這篇文章主要介紹了c++截取漢字英文混合字符串,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • 數(shù)據(jù)結(jié)構(gòu)之紅黑樹詳解

    數(shù)據(jù)結(jié)構(gòu)之紅黑樹詳解

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之紅黑樹詳解,紅黑樹是一種自平衡二叉查找樹,它的統(tǒng)計性能要好于平衡二叉樹(AVL樹),因此,紅黑樹在很多地方都有應用,需要的朋友可以參考下
    2014-08-08
  • C++實現(xiàn)簡單版通訊錄管理系統(tǒng)

    C++實現(xiàn)簡單版通訊錄管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現(xiàn)簡單版通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 詳解C語言中的指針與數(shù)組的定義與使用

    詳解C語言中的指針與數(shù)組的定義與使用

    這篇文章主要介紹了C語言中的指針與數(shù)組的定義與使用,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12
  • C++函數(shù)模板與類模板實例解析

    C++函數(shù)模板與類模板實例解析

    這篇文章主要介紹了C++函數(shù)模板與類模板,需要的朋友可以參考下
    2014-08-08
  • 如何使用C語言實現(xiàn)細菌的繁殖與擴散

    如何使用C語言實現(xiàn)細菌的繁殖與擴散

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)細菌的繁殖與擴散,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • C++學習筆記std::vector底層原理及擴容

    C++學習筆記std::vector底層原理及擴容

    這篇文章主要為大家介紹了C++學習之std::vector底層原理及擴容詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • C語言 循環(huán)詳解及簡單代碼示例

    C語言 循環(huán)詳解及簡單代碼示例

    本文主要介紹C語言的循環(huán)知識,這里整理了循環(huán)的基礎(chǔ)資料并附簡單的代碼示例詳細講解,有需要的小伙伴可以參考下
    2016-08-08

最新評論

沧州市| 武平县| 广安市| 望都县| 梁河县| 荃湾区| 玛纳斯县| 民县| 舟山市| 崇文区| 满洲里市| 开化县| 来安县| 沙洋县| 芦溪县| 阿荣旗| 保靖县| 托克托县| 甘肃省| 莱阳市| 城固县| 长岛县| 桐城市| 清镇市| 个旧市| 崇明县| 旌德县| 余姚市| 石台县| 巴林右旗| 交口县| 海兴县| 广南县| 南雄市| 井研县| 汉阴县| 清水县| 正安县| 娱乐| 南陵县| 乐昌市|