第9篇:C++ 繼承鏈的構(gòu)造過(guò)程和內(nèi)存布局

在本文中,我們將繼續(xù)深入研究C ++運(yùn)行時(shí)動(dòng)態(tài)調(diào)度的相關(guān)話(huà)題功偿。 到目前為止恨旱,我們已經(jīng)驗(yàn)證gdb不會(huì)Trivial類(lèi)型的類(lèi)和默認(rèn)構(gòu)造函數(shù)創(chuàng)建虛擬表,我們?cè)诒酒獙㈥U述非虛擬派生類(lèi)它們的構(gòu)造和內(nèi)存布局若专,這是正確區(qū)分虛擬類(lèi)和非虛擬類(lèi)在內(nèi)存分配方面的差異的前提许蓖。

示例導(dǎo)入

讓我引入兩個(gè)新的類(lèi)。一個(gè)基類(lèi)Person和一個(gè)從Person繼承的派生類(lèi)Student调衰。請(qǐng)注意膊爪,兩者都使用其方法who()的相同簽名。

  • 有了動(dòng)態(tài)調(diào)度后嚎莉,將查詢(xún)vtable并調(diào)用適當(dāng)?shù)姆椒ā?/li>
  • 如果沒(méi)有動(dòng)態(tài)分配米酬,則將調(diào)用與對(duì)象的指針類(lèi)型匹配的方法。 Student *m將調(diào)用Student:: who()趋箩,而Person *p將調(diào)用Person::who()赃额。讓我們用匯編代碼驗(yàn)證一下。
#include <stdio.h>

class Person {
public:
    Person() {}
    void who() {
        printf("I am a human!!\n");
    }

};

class Student: public Person{
public:
    Student() {}
    void career() {
        printf("I am a student!!\n");
    }
};

int main() {
    Student *m = new Student();
    m->who();

    Person *p = m;
    p->who();
}

我們看到who()方法已經(jīng)在編譯時(shí)已經(jīng)植入Student類(lèi)的上下文叫确,從callq 0400658 <_ZN7Student3whoEv>,這條指令表明運(yùn)行時(shí)決策是不可能的跳芳。 指針的類(lèi)型在編譯時(shí)是已知的,編譯器會(huì)選擇正確的who()方法調(diào)用,同理callq 0x400626 <_ZN6Person3whoEv>,也是一條編譯時(shí)的靜態(tài)指令竹勉。下圖說(shuō)明類(lèi)一切:


但是我們也可以通過(guò)定義基類(lèi)的名稱(chēng)空間來(lái)調(diào)用基類(lèi)的方法飞盆,如下所示

m->Person::who()

內(nèi)存布局

為了降低問(wèn)題的復(fù)雜性,我將使用此代碼的一些細(xì)微變化次乓,去掉一些who方法:

class Person {
public:
    int age=6;
    Person() {}
};

class Student: public Person{
public:
    int idNo=1000;
    Student() {}
};

為了了解虛擬表的隱藏位置吓歇,讓我們首先檢查一下簡(jiǎn)單繼承層次結(jié)構(gòu)的內(nèi)存布局。 讓我們向Student和Person類(lèi)添加一些整數(shù)變量票腰。 使用此特定編譯器的特定計(jì)算機(jī)上的sizeof(int)為4個(gè)字節(jié)城看。 但總是要記住,這個(gè)數(shù)字在不同硬件上尺寸可能不一樣杏慰。我們?cè)趃db中使用print命令輸出代碼中相關(guān)變量的地址,如下所示测柠。

(gdb) p &m
$2 = (Student **) 0x7fffffffe2e8
(gdb) p &p
$3 = (Person **) 0x7fffffffe2e0
(gdb) p *m
$4 = {<Person> = {age = 6}, idNo = 1000}
(gdb) p *p
$5 = {age = 6}
(gdb) p m
$6 = (Student *) 0x602010
(gdb) p p
$7 = (Person *) 0x602030
(gdb) 

我們用繪制成如下圖


以低數(shù)字開(kāi)頭的內(nèi)存位置(在本例中為0x602010)是在堆上分配給Student對(duì)象的炼鞠。由main棧幀上的指針變量m(地址0x7fffffffe2e8)指向它。如此類(lèi)推Person對(duì)象也在堆上的區(qū)域是0x602030的位置鹃愤。 眾所周知簇搅,堆向上增長(zhǎng),而棧向下增長(zhǎng)软吐。 對(duì)象在堆和棧幀的的組織方式在很大程度上取決于所使用的編譯器和操作系統(tǒng)的內(nèi)存管理方式瘩将。

某些值可以完全優(yōu)化并不需要入棧,直接并用寄存器代替。但本示例中沒(méi)有使用任何編譯的優(yōu)化選項(xiàng),因此仍然使用x86的約定組織程序棧凹耙。

從上面的內(nèi)存布局中姿现,我們可以得出幾點(diǎn)啟示:

  • 基類(lèi)指針p和派生指針都分別類(lèi)的第一個(gè)字節(jié)⌒けВ基類(lèi)之后是派生類(lèi)备典,位于更高的地址處。這個(gè)簡(jiǎn)單示例有幫于我們找到虛擬指針意述。
  • 派生類(lèi)的內(nèi)存分配一般來(lái)說(shuō)會(huì)比父類(lèi)的內(nèi)存分配要大,因?yàn)?strong>派生類(lèi)會(huì)從基類(lèi)中繼承(拷貝)了public和protected修飾的數(shù)據(jù)成員的副本提佣。在本例中Student對(duì)象得到了Person對(duì)象的age副本,當(dāng)你嘗試使用sizeof(*m)得到的結(jié)果是8,而Person的內(nèi)存分配尺寸則是4.

繼承鏈中的構(gòu)造順序

這其實(shí)是前面文章談?wù)摰蕉嗬^承的RAII約定荤崇,我們從反匯編的角度,加深對(duì)此過(guò)程的了解


派生類(lèi)的初始化過(guò)程

  1. 在當(dāng)前派生類(lèi)構(gòu)造函數(shù)的上下文的按照繼承列表中的順序執(zhí)行依次初始化繼承鏈中各個(gè)父類(lèi)的構(gòu)造函數(shù),本示例如下步驟拌屏。
  • (1) 從main函數(shù)執(zhí)行call 0x400640指令后,進(jìn)入Student::Student()所在代碼段中的上下文執(zhí)行的一些入棧的操作(構(gòu)造函數(shù)棧內(nèi)存分配以及狀態(tài)保存)。
  • (2) 執(zhí)行call 0x44062c 即在Person類(lèi)指令集所在的代碼段地址初始化,在基類(lèi)構(gòu)造返回之前,匯編指令movl $0x6,(%rax),這個(gè)干了這些事:基類(lèi)將數(shù)據(jù)成員-變量age=6作為返回值保存到rax寄存器中緩存的內(nèi)存地址指向的位置(該位置在前一步的Student::Student構(gòu)造函數(shù)的棧內(nèi)存分配了,即-0x8(%rbp)的位置),以便派生類(lèi)Student對(duì)象的構(gòu)造函數(shù)讀取作為它的數(shù)據(jù)成員术荤。
  1. 返回派生類(lèi)本身的構(gòu)造函數(shù)執(zhí)行剩余的指令集倚喂。
    繼承的初始化過(guò)程

垃圾回收的過(guò)程 ,和繼承列表中定義的父類(lèi)順序相反。

  • 首先,調(diào)用函數(shù)在結(jié)束之時(shí)隱式執(zhí)行子類(lèi)的解構(gòu)函數(shù)瓣戚。
  • 然后,依次逆序執(zhí)行子類(lèi)繼承列表中父類(lèi)的解構(gòu)函數(shù)端圈。

從匯編代碼可知,在每個(gè)構(gòu)造函數(shù)的的匯編上下文,在執(zhí)行retq指令返回之前,當(dāng)前的構(gòu)造函數(shù)已經(jīng)將初始化的一些局部變量緩存到可用的寄存器中緩存的內(nèi)存地址所指向的位置了,當(dāng)然通常是rax寄存器子库。

小結(jié)

不對(duì)派生類(lèi)的成員進(jìn)行任何更改而優(yōu)先初始化基類(lèi)的構(gòu)造函數(shù)舱权。 這對(duì)引入虛擬表時(shí)是一個(gè)非常重要的概念,因?yàn)榇隧樞蚨x了什么函數(shù)在什么階段可見(jiàn)仑嗅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刑巧,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子无畔,更是在濱河造成了極大的恐慌,老刑警劉巖吠冤,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浑彰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拯辙,警方通過(guò)查閱死者的電腦和手機(jī)郭变,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)颜价,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人诉濒,你說(shuō)我怎么就攤上這事周伦。” “怎么了未荒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵专挪,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我片排,道長(zhǎng)寨腔,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任率寡,我火速辦了婚禮迫卢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冶共。我一直安慰自己乾蛤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布捅僵。 她就那樣靜靜地躺著家卖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪命咐。 梳的紋絲不亂的頭發(fā)上篡九,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音醋奠,去河邊找鬼榛臼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛窜司,可吹牛的內(nèi)容都是我干的沛善。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼塞祈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼金刁!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起议薪,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤尤蛮,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后斯议,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體产捞,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年哼御,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坯临。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焊唬。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖看靠,靈堂內(nèi)的尸體忽然破棺而出赶促,到底是詐尸還是另有隱情,我是刑警寧澤挟炬,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布鸥滨,位于F島的核電站,受9級(jí)特大地震影響辟宗,放射性物質(zhì)發(fā)生泄漏爵赵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一泊脐、第九天 我趴在偏房一處隱蔽的房頂上張望空幻。 院中可真熱鬧,春花似錦容客、人聲如沸秕铛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)但两。三九已至,卻和暖如春供置,著一層夾襖步出監(jiān)牢的瞬間谨湘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工芥丧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留紧阔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓续担,卻偏偏與公主長(zhǎng)得像擅耽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子物遇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355