[轉(zhuǎn)]深入探索C++對象模型(2)

上一章講過了關于類對象內(nèi)存分布,對于nostatic數(shù)據(jù)將會放在對象內(nèi)存空間中惊橱,static數(shù)據(jù)成員和nostatic局荚、static函數(shù)成員將不會放在對象內(nèi)存中,對于虛擬繼承和含有虛函數(shù)的類來說钞澳,將會在對象內(nèi)存中增加一個虛表指針怠惶,指向該類的虛表,其中虛表中將會存放虛函數(shù)的地址和虛擬基類的地址轧粟。一個類中只含有一個共享虛表(繼承基類的虛表也是繼承類的虛表策治,一般繼承類的虛函數(shù)會存放在第一個基類的虛表中方便提取)兰吟,對象中可以含有多個虛指針通惫,繼承至基類,除了直接虛擬繼承的繼承類才會產(chǎn)生新的vptr和虛表用于指示虛擬基類的位置混蔼。

下面是如何構建類對象履腋,即構造函數(shù)的深入探索。

首先強調(diào)兩個C++新手容易陷入的誤區(qū):

(1)編譯器會為沒有顯示聲明構造函數(shù)的類合成默認構造函數(shù)惭嚣;-------------并不是所有的都合成默認構造函數(shù)遵湖,只有只有四種情況下才會合成,其他情況下不會合成料按。

(2)編譯器合成出來的默認構造函數(shù)會為每個數(shù)據(jù)成員賦予默認值奄侠。------------編譯器不負責對數(shù)據(jù)成員的賦值工作,并不是編譯器所需的载矿,而應該是程序員完成的工作垄潮。(除非其有默認構造函數(shù)的subobject和memeber在構造時會初始化)

那么烹卒,哪四種情況會導致編譯器為類合成一個默認的構造函數(shù)呢?

(1)類成員存在默認構造函數(shù)的弯洗,在合成默認構造函數(shù)時必須調(diào)用該成員的默認構造函數(shù)或者是擴充到已有的默認構造函數(shù)中旅急;

(2)所繼承的基類存在默認構造函數(shù)的,編譯器會合成默認構造函數(shù)牡整,在構造函數(shù)中調(diào)用基類默認構造函數(shù)藐吮;

(3)含有虛函數(shù)的類,編譯器會合成默認構造函數(shù)逃贝,主要是因為要初始化虛表指針谣辞;

(4)虛繼承的類,編譯器會合成默認構造函數(shù)沐扳,也是因為要初始化虛表指針泥从,指向虛擬基類對象。

當已經(jīng)顯示定義了構造函數(shù)沪摄,即使不是默認的構造函數(shù)躯嫉,編譯器也不會為類合成默認構造函數(shù)。如果顯示定義的構造函數(shù)沒有完成一些默認構造的功能杨拐,編譯器將會擴充顯示定義的構造函數(shù)祈餐,調(diào)用能夠調(diào)用的默認構造函數(shù)比如成員默認構造函數(shù)或基類默認構造函數(shù)。

復制構造函數(shù)深度探索:

復制構造函數(shù)只要用于三個方面:用一個對象的內(nèi)容構造出一個新對象哄陶;參數(shù)以值傳遞的形式傳遞時帆阳;以一個對象作為返回值時。在這三種情況下將會調(diào)用復制構造函數(shù)奕筐,有的情況下需要產(chǎn)生臨時對象舱痘。

例如:string s=a;將不會產(chǎn)生臨時對象离赫,string s=string(a);將會產(chǎn)生臨時對象t,然后在調(diào)用復制構造函數(shù)構造s塌碌;string s(a);也不會產(chǎn)生臨時對象渊胸。

當參數(shù)和返回值以值傳遞的形式,一般情況下都會產(chǎn)生臨時對象存儲數(shù)據(jù)台妆,當在編譯器NRV優(yōu)化(下面細講)的情況下翎猛,返回值可能不要產(chǎn)生臨時對象婶熬,而是直接操作畏梆,例如:

<pre>
T operator+(T &a,T &b); Tc=a+b;
</pre>

在編譯器優(yōu)化的情況下焕济,operator +函數(shù)極有可能是這個樣子的:
<pre>
//編譯器內(nèi)部偽碼
void operator +(T &a,T &b,T &c)
{

//直接對c進行操作芥被,這種情況下不需要產(chǎn)生構造對象

}
</pre>

復制構造的兩種方式:bitwise和memeberwise族展,前者是單純的位拷貝届腐,后者則是以成員為單位進行遞歸拷貝(所謂遞歸拷貝就是當成員為一個類對象時將會按照該對象的成員進行拷貝知道結束)鸵隧。另外粉臊,復制構造函數(shù)也會對數(shù)組的所有成員進行拷貝。

當類中沒有顯示定義的復制構造函數(shù)的時候遗座,編譯器會不會合成一個復制構造函數(shù)呢舀凛?要根據(jù)該類的復制構造形式而定,bitwise形式不會合成復制構造函數(shù)途蒋,memberwise形式會合成猛遍。

以下有四種情況會導致類不是bitwise形式,會在無顯示定義復制構造函數(shù)的時候合成默認復制構造函數(shù):

(1)類成員對象含所有顯示復制構造函數(shù)(無論是編譯器合成的還是自定義的号坡,有時候為了實現(xiàn)NRV優(yōu)化會自定義復制構造函數(shù))懊烤,編譯器會為這樣的類合成默認復制構造函數(shù);

(2)類的基類對象含有復制構造函數(shù)宽堆,編譯器會構造默認復制構造函數(shù)奸晴,構造的時候?qū){(diào)用擴充基類復制構造函數(shù);

(3)類中含有虛函數(shù)的日麸,編譯器將會構造默認復制構造函數(shù)寄啼,這樣可以保證當基類對象被繼承類對象初始化的時候,基類對象的vptr指向正確的虛函數(shù)表代箭,而不能單純的復制vptr那么容易墩划,否則將會造成基類對象的vptr指向繼承類的虛表了;

(4)虛擬繼承的類嗡综,編譯器將會構造默認復制構造函數(shù)乙帮,同樣是當基類對象被子類對象初始化時,必須保證指向虛擬基類對象的指針被正確設置极景,因為虛擬基類對象在不同子類內(nèi)存中的位置是不同的察净,在虛表中對應offset值是不同的,所以必須保證vptr所指向的虛表的正確性盼樟,因此編譯器將會為虛擬繼承的類合成默認復制構造函數(shù)氢卡。

NRV編譯器優(yōu)化:前提是編譯器提供該服務,且類中存在復制構造函數(shù)晨缴,無論是編譯器合成的還是自定義的译秦,否則優(yōu)化無從談起,因為trival復制構造函數(shù)就是最有效率的構造方法击碗,有些類為了使用NRV優(yōu)化甚至強行提供顯示復制構造函數(shù)筑悴。

NRV優(yōu)化主要用在返回值為對象的函數(shù)身上,主要方法是減少臨時對象的數(shù)目來提高效率稍途。

優(yōu)化前:
<pre>
X bar()//將會產(chǎn)生臨時對象存儲返回的值
{

X xx;

//處理

return xx;

}
</pre>

優(yōu)化后:
<pre>
void bar(X &_result)//這樣在指定函數(shù)返回對象是將不會產(chǎn)生臨時對象

{

X xx;

//處理xx

//調(diào)用復制構造函數(shù)

_result.X::X(xx);

return阁吝;

}
</pre>

X yy=bar();相當于bar(yy);

但是,當bar()單獨使用的時候械拍,仍然會產(chǎn)生一個臨時對象存儲結果突勇,因為函數(shù)沒有返回的對象装盯。

而NRV優(yōu)化又是在上述的基礎上直接對_result上處理,不需要在函數(shù)內(nèi)部產(chǎn)生一個xx對象与境,然后再調(diào)用復制構造函數(shù)验夯,這樣就減省了復制構造的消耗,但是會使用默認構造函數(shù)摔刁。
<pre>
void bar(X &_result)//NRV優(yōu)化

{

_result.X::X();

//對_result直接進行處理

return;

}
</pre>

成員初始化表的使用:

有以下三種情況必須使用成員初始化表:

(1)調(diào)用基類構造函數(shù)或者基類復制構造函數(shù)

(2)類對象中的引用初始化

(3)類中const對象的初始化

其成員初始化既可以使用成員初始化列表挥转,也可以使用普通的方式,但還是普通方式效率太低共屈。

普通初始化方式:首先將成員默認構造绑谣,然后產(chǎn)生一個臨時對象使用復制構造參數(shù)對象,最后使用賦值方式初始化拗引;

而成員初始化列表則會在用戶代碼之前按照成員聲明的順序并按相應的方式進行初始化借宵。

注意:

成員初始化列表中的順序不決定初始化順序,成員初始化順序由成員聲明的順序而定矾削;

成員初始化列表中的內(nèi)容先于用戶代碼執(zhí)行

原文地址:http://www.cnblogs.com/tracylee/archive/2012/12/18/2824125.html

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末壤玫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子哼凯,更是在濱河造成了極大的恐慌欲间,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件断部,死亡現(xiàn)場離奇詭異猎贴,居然都是意外死亡,警方通過查閱死者的電腦和手機蝴光,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門她渴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蔑祟,你說我怎么就攤上這事趁耗。” “怎么了做瞪?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵对粪,是天一觀的道長。 經(jīng)常有香客問我装蓬,道長,這世上最難降的妖魔是什么纱扭? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任牍帚,我火速辦了婚禮,結果婚禮上乳蛾,老公的妹妹穿的比我還像新娘暗赶。我一直安慰自己鄙币,他們只是感情好,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布蹂随。 她就那樣靜靜地躺著十嘿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪岳锁。 梳的紋絲不亂的頭發(fā)上绩衷,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音激率,去河邊找鬼咳燕。 笑死,一個胖子當著我的面吹牛乒躺,可吹牛的內(nèi)容都是我干的招盲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嘉冒,長吁一口氣:“原來是場噩夢啊……” “哼曹货!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起讳推,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤顶籽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后娜遵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜕衡,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年设拟,在試婚紗的時候發(fā)現(xiàn)自己被綠了慨仿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡纳胧,死狀恐怖镰吆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情跑慕,我是刑警寧澤万皿,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站核行,受9級特大地震影響牢硅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜芝雪,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一减余、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惩系,春花似錦位岔、人聲如沸如筛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杨刨。三九已至,卻和暖如春擦剑,著一層夾襖步出監(jiān)牢的瞬間妖胀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工抓于, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留做粤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓捉撮,卻偏偏與公主長得像怕品,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子巾遭,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一個博客肉康,這個博客記錄了他讀這本書的筆記,總結得不錯灼舍『鸷停《深度探索C++對象模型》筆記匯總 1. C++對象模型與內(nèi)...
    Mr希靈閱讀 5,585評論 0 13
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,517評論 1 51
  • 本文博客園地址:http://www.cnblogs.com/xiongxuanwen/p/4290086.htm...
    先之閱讀 841評論 0 5
  • 1. 結構體和共同體的區(qū)別。 定義: 結構體struct:把不同類型的數(shù)據(jù)組合成一個整體骑素,自定義類型炫乓。共同體uni...
    breakfy閱讀 2,124評論 0 22
  • 陽光正好末捣,我想去春游。我想坐在綠綠的草地上想心事创橄,我想看孩子們追逐打鬧箩做,我想把這愜意的時光都拍下來。而事實上妥畏,我只...
    她是妖孽閱讀 393評論 0 0