深度探索C++對(duì)象模型-第三章

說(shuō)明:

? <u>不是很清楚的點(diǎn)</u>,用下劃線。

? 解答,用斜體;

? 重點(diǎn),用粗體加粗;

第二章 Data 語(yǔ)意學(xué)

3.2 Data Member 的布局

非靜態(tài)成員變量在class object中的排列順序?qū)⒑推渎暶鞯捻樞蛞粯拥?。但C++ standard允許編譯器將多個(gè)access sections之中的data members自由排列,不必在乎他們的出現(xiàn)在class中的聲明順序。

3.3 Data Member 的存取

存取代價(jià):

每一個(gè)member 的存取許可(private public protected),以及與class的關(guān)聯(lián),并不會(huì)導(dǎo)致任何空間上或執(zhí)行時(shí)間上的額外負(fù)擔(dān)——不論是在個(gè)別的class objects 或是在static data member 本身。

static data members:

靜態(tài)數(shù)據(jù)成員(static data members) 被視為global變量,只有一個(gè)實(shí)體,存放在程序的data segment(數(shù)據(jù)段)之中,每次取static member 就會(huì)被內(nèi)部轉(zhuǎn)化為對(duì)該唯一的extern 實(shí)體的直接參考操作。若取一個(gè)static data member的地址,會(huì)得到一個(gè)數(shù)據(jù)類型的指針,而不是只想起class member的指針。

nonstatic data members:

只要程序猿在一個(gè)成員函數(shù)中直接處理一個(gè)非靜態(tài)成員變量,“隱式類對(duì)象(this指針)”就會(huì)出現(xiàn)。

欲對(duì)一個(gè)nonstatic data member 進(jìn)行存取操作,編譯器需要把this指針(class object的起始地址)加上data member的偏移量。

非靜態(tài)成員變量的偏移量在編譯時(shí)期就可以獲知,即使這個(gè)成員變量是屬于被派生的類。因此存取效率高。

3.4 繼承 與 Data Member

class Point2d{
    public:
        //....
    private:
        float x,y;
}
class Point3d{
    public:
        //....
    private:
        float x,y,z;
}

討論上述結(jié)構(gòu),與 “提供繼承結(jié)構(gòu)” 有什么不同?

下面分四種情況討論。

1.只要繼承不要多態(tài)

具體繼承(相對(duì)于虛擬繼承)并不會(huì)增加空間或存儲(chǔ)時(shí)間上的額外負(fù)擔(dān)。這種情況base class和derived class的objects都是從相同的地址開始,其差異只在于derived object 比較大,用以容納自建的靜態(tài)數(shù)據(jù)成員,把一個(gè)derived class object指定給base class 的指針或引用(一定要通過(guò)指針或引用),并不需要編譯器去調(diào)?;蛐薷牡刂?,提供了最佳執(zhí)行效率。

2.加上多態(tài)

即在繼承關(guān)系中,提供一個(gè)虛函數(shù)接口。

多態(tài)帶來(lái)了程序彈性,但這種彈性會(huì)帶來(lái)空間和存取時(shí)間的額外負(fù)擔(dān):

  1. 導(dǎo)入一個(gè)虛函數(shù)表 ,用來(lái)存儲(chǔ)它所聲明的每一個(gè)virtual functions的地址。
  2. 在每一個(gè)class object中導(dǎo)入一個(gè)vptr,提供執(zhí)行期的鏈接,使每一個(gè)object能夠找到相應(yīng)的virtual table。
  3. 加強(qiáng)constructor,使它能夠?yàn)関ptr設(shè)定初始值,讓它指向class 所對(duì)應(yīng)的virtual table 。
  4. 加強(qiáng)destructor,使它能夠消抹“指向class 相關(guān)virtual table”的vptr。

3.多重繼承

class point2d{
    public:
        //....
    protected:
        float x,y;
}
class Vertex{
    public:
        //....
    protected:
        Vertex *next;
}
class Vertex3d: public point2d, public Vertex{ 
    //point2d是第一個(gè)base class,Vertex是第二個(gè)
    public:
        //....
    protected:
        float mumble;
}

對(duì)于一個(gè)多重派生對(duì)象,將其地址指定給“最左端(第一個(gè))base class的指針”,情況和單一繼承時(shí)相同,因?yàn)槎叨贾赶蛄讼嗤钠鹗嫉刂?;至于第二個(gè)或后面的base class 的地址指定操作,則需要將地址修改過(guò):加上(<u>或減去,如果是downcast</u>)介于中間的base class subobject(s)的大小。

如果要存取第二個(gè)(或后面)的base class 中的一個(gè)data member ,會(huì)增加額外的成本嗎?不需要付出額外的成本,因?yàn)閙embers的位置在編譯時(shí)期就固定了,因此存取member只是一個(gè)簡(jiǎn)單的offset的運(yùn)算。

4.虛擬繼承

這一部分比較難,并且各個(gè)編譯器實(shí)現(xiàn)方式不同。

class如果含有一個(gè)或多個(gè) virtual base class subobjects 將被分割為兩部分:一個(gè)不變區(qū)域和一個(gè)共享區(qū)域。

  • 不變區(qū)域中的數(shù)據(jù),不管后繼如何衍化,總是能有固定的offset,這部分可以被直接存??;
  • 至于共享區(qū)域,所表現(xiàn)的就是virtual base class subobject ,這個(gè)部分?jǐn)?shù)據(jù),其位置因?yàn)槊看闻缮僮鞫凶兓?,所以只能間接存取。

總結(jié):

虛擬繼承內(nèi)存布局
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容