亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

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

2020-01-26 15:21:13
字體:
來源:轉載
供稿:網友

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

一、Primer中的說法

首先我們現來看看經典是怎么說的:

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

還有一段這樣說:

通常直接初始化和復制初始化僅在低級別優化上存在差異,然而,對于不支持復制的類型,或者使用非explicit構造函數的時候,它們有本質區別

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

二、通常的誤解

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

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

#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; } 

輸出結果為:

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

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

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

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

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

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

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; } 

然而你還是非常遺憾地發現,還是沒有編譯通過。這是為什么呢?從上面的語句和之前的運行結果來看,的確是已經沒有調用復制構造函數了,為什么還是編譯錯誤呢?

經過實驗,main函數只有這樣才能通過編譯:

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

在這里我們可以看到,原來是復制構造函數欺騙了我們。

四、揭開真相

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

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

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

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

現在我們再來看回main函數中的語句:

1、ClassTest ct1("ab");這條語句屬于直接初始化,它不需要調用復制構造函數,直接調用構造函數ClassTest(const char *pc),所以當復制構造函數變為私有時,它還是能直接執行的。

2、ClassTest ct2 = "ab";這條語句為復制初始化,它首先調用構造函數ClassTest(const char *pc)函數創建一個臨時對象,然后調用復制構造函數,把這個臨時對象作為參數,構造對象ct2;所以當復制構造函數變為私有時,該語句不能編譯通過。

3、ClassTest ct3 = ct1;這條語句為復制初始化,因為ct1本來已經存在,所以不需要調用相關的構造函數,而直接調用復制構造函數,把它值復制給對象ct3;所以當復制構造函數變為私有時,該語句不能編譯通過。

4、ClassTest ct4(ct1);這條語句為直接初始化,因為ct1本來已經存在,直接調用復制構造函數,生成對象ct3的副本對象ct4。所以當復制構造函數變為私有時,該語句不能編譯通過。

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

5、ClassTest ct5 = ClassTest();這條語句為復制初始化,首先調用默認構造函數產生一個臨時對象,然后調用復制構造函數,把這個臨時對象作為參數,構造對象ct5。所以當復制構造函數變為私有時,該語句不能編譯通過。

五、假象產生的原因

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

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

ClassTest ct2 = "ab";

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

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

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

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
激情成人在线视频| 在线日韩精品视频| 亚洲加勒比久久88色综合| 最近2019中文字幕mv免费看| 国产精品主播视频| 久久久国产影院| 91精品国产91久久久久| 91久久国产婷婷一区二区| 国产性色av一区二区| 欧美性xxxxx极品娇小| 日韩电影中文字幕在线| 国产v综合v亚洲欧美久久| 欧美二区乱c黑人| 亚洲综合一区二区不卡| 欧美中文字幕视频在线观看| 伊人亚洲福利一区二区三区| 午夜精品一区二区三区在线| 欧美成人精品三级在线观看| 亚洲va久久久噜噜噜久久天堂| 97国产精品视频| 97国产真实伦对白精彩视频8| 亚洲a一级视频| 日韩成人在线网站| 日韩中文有码在线视频| 亚洲欧美精品在线| 国产91网红主播在线观看| 日韩欧美亚洲国产一区| 精品国产一区二区三区久久狼黑人| 日韩欧美国产视频| 亚洲成人教育av| 国模精品一区二区三区色天香| 亚洲区一区二区| 亚洲成人精品在线| 51午夜精品视频| 播播国产欧美激情| 国产日韩精品入口| 亚洲国产精品999| 欧美日韩国产精品| 欧美日韩一区二区精品| 亚洲欧洲一区二区三区久久| 在线视频免费一区二区| 亚洲美腿欧美激情另类| 精品国产一区二区三区久久久狼| 国产裸体写真av一区二区| 亚洲香蕉成视频在线观看| 亚洲a级在线观看| 一区二区在线视频播放| 亚洲精品白浆高清久久久久久| 欧美一区第一页| 免费成人高清视频| 欧美理论电影在线播放| 大伊人狠狠躁夜夜躁av一区| 国产精品视频色| 欧美成人亚洲成人| 一区二区三区国产在线观看| 欧美中文字幕精品| 欧美成人剧情片在线观看| 欧美极品美女视频网站在线观看免费| 亚洲va久久久噜噜噜久久天堂| 2021国产精品视频| 欧美体内谢she精2性欧美| 欧美日韩爱爱视频| 粉嫩老牛aⅴ一区二区三区| 欧美大学生性色视频| 国产精品黄色影片导航在线观看| 亚洲国产欧美一区二区丝袜黑人| 91久久嫩草影院一区二区| 精品国偷自产在线| 亚洲va久久久噜噜噜久久天堂| 欧美性猛交视频| 伊人久久久久久久久久久久久| 国产日韩欧美另类| 91天堂在线观看| 日韩激情视频在线| 亚洲午夜未删减在线观看| 国产日韩中文字幕在线| 51视频国产精品一区二区| 亚洲国产精品电影在线观看| 日韩电影免费观看在线| 国产精品青草久久久久福利99| 久久久久久中文| 丰满岳妇乱一区二区三区| 国产精品久久久久久久天堂| 久久99精品久久久久久青青91| 一区二区三区回区在观看免费视频| 色老头一区二区三区| 亚洲国产精品va在看黑人| 69精品小视频| 亚洲精品福利免费在线观看| 浅井舞香一区二区| 亚洲老头同性xxxxx| 菠萝蜜影院一区二区免费| 欧美黄色成人网| 国产精品久久久久久久9999| 欧美国产欧美亚洲国产日韩mv天天看完整| 国产在线拍揄自揄视频不卡99| 亚洲精品一区av在线播放| 国产视频一区在线| 九九九热精品免费视频观看网站| 91精品国产自产在线老师啪| 中国china体内裑精亚洲片| 91视频国产一区| 性欧美视频videos6一9| 久久韩剧网电视剧| 亚洲人成电影网站| wwwwwwww亚洲| 国产精品久久久久久亚洲影视| 欧美日韩国产丝袜另类| 亚洲精品国偷自产在线99热| 亚洲成人aaa| 欧美黑人狂野猛交老妇| 中文字幕日本精品| 欧美日韩国内自拍| 国产精品高潮呻吟久久av无限| 成人亚洲综合色就1024| 一本一本久久a久久精品综合小说| 欧美成人h版在线观看| 亚洲人成电影在线观看天堂色| 国产精品都在这里| 精品国产精品自拍| 国产精品视频久久久久| 欧美激情视频网址| 亚洲国产精彩中文乱码av在线播放| 久久久av免费| 国模私拍一区二区三区| 国内精品400部情侣激情| 国模精品一区二区三区色天香| 亚洲欧美综合区自拍另类| 亚洲欧美精品suv| 欧美高清视频在线播放| 另类少妇人与禽zozz0性伦| 国产精品视频999| 国产视频福利一区| 久久久国产91| 国产精品视频久| 国产成人精品最新| 成人妇女淫片aaaa视频| 日韩精品高清在线观看| 亚洲大胆人体在线| 久久久久免费精品国产| 亚州国产精品久久久| 日韩欧美在线看| 性日韩欧美在线视频| 国内精品久久影院| 亚洲精品久久在线| 精品小视频在线| 亚洲国产精品久久久| 4438全国成人免费| 久久久久久久影院| 欧美极品少妇xxxxⅹ裸体艺术| 成人在线激情视频| 亚洲视频一区二区| 欧美日韩成人网| 欧美区二区三区| 久久久久久久久久久国产| 欧美最顶级丰满的aⅴ艳星| 亚洲一区二区三区香蕉| 日韩在线观看电影| 欧美激情三级免费| 欧美一级片久久久久久久| 91国产精品视频在线| 九九精品在线观看| 国产日韩在线精品av| 久久久久久国产|