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

解析C++類內(nèi)存分布

 更新時間:2021年06月09日 09:10:40   作者:lsgxeva  
本篇文章介紹了C++類內(nèi)存分布結(jié)構(gòu),我們來看看編譯器是怎么處理類成員內(nèi)存分布的,特別是在繼承、虛函數(shù)存在的情況下

工欲善其事,必先利其器,我們先用好Visual Studio工具,像下面這樣一步一步來:

先選擇左側(cè)的C/C++->命令行,然后在其他選項這里寫上/d1 reportAllClassLayout,它可以看到所有相關(guān)類的內(nèi)存布局,如果寫上/d1 reportSingleClassLayoutXXX(XXX為類名),則只會打出指定類XXX的內(nèi)存布局。近期的VS版本都支持這樣配置。

下面可以定義一個類,像下面這樣:

class Base
{
    int a;
    int b;
public:
    void CommonFunction();
};

然后編譯一下,可以看到輸出框里面有這樣的排布:

這里不想花精力在內(nèi)存對齊因素上,所以成員變量都設(shè)為int型。

從這里可以看到普通類的排布方式,成員變量依據(jù)聲明的順序進行排列(類內(nèi)偏移為0開始),成員函數(shù)不占內(nèi)存空間。

再看下繼承,往后面添加如下代碼:

class DerivedClass: public Base
{
    int c;
public:
    void DerivedCommonFunction();
};

編譯,然后看到如下的內(nèi)存分布(父類的內(nèi)存分布不變,這里只討論子類成員變量的內(nèi)存分布):

可以看到子類繼承了父類的成員變量,在內(nèi)存排布上,先是排布了父類的成員變量,接著排布子類的成員變量,同樣,成員函數(shù)不占字節(jié)。

下面給基類加上虛函數(shù),暫時注釋掉DerivedClass,看一下這時的內(nèi)存排布:

class Base
{
    int a;
    int b;
public:
    void CommonFunction();
    void virtual VirtualFunction();
};

這個內(nèi)存結(jié)構(gòu)圖分成了兩個部分,上面是內(nèi)存分布,下面是虛表,我們逐個看。VS所帶編譯器是把虛表指針放在了內(nèi)存的開始處(0地址偏移),然后再是成員變量;下面生成了虛表,緊跟在&Base1_meta后面的0表示,這張?zhí)摫韺?yīng)的虛指針在內(nèi)存中的分布,下面列出了虛函數(shù),左側(cè)的0是這個虛函數(shù)的序號,這里只有一個虛函數(shù),所以只有一項,如果有多個虛函數(shù),會有序號為1,為2的虛函數(shù)列出來。

編譯器是在構(gòu)造函數(shù)創(chuàng)建這個虛表指針以及虛表的。

那么編譯器是如何利用虛表指針與虛表來實現(xiàn)多態(tài)的呢?是這樣的,當(dāng)創(chuàng)建一個含有虛函數(shù)的父類的對象時,編譯器在對象構(gòu)造時將虛表指針指向父類的虛函數(shù);同樣,當(dāng)創(chuàng)建子類的對象時,編譯器在構(gòu)造函數(shù)里將虛表指針(子類只有一個虛表指針,它來自父類)指向子類的虛表(這個虛表里面的虛函數(shù)入口地址是子類的)。

所以,如果是調(diào)用Base *p = new Derived();生成的是子類的對象,在構(gòu)造時,子類對象的虛指針指向的是子類的虛表,接著由Derived*到Base*的轉(zhuǎn)換并沒有改變虛表指針,所以這時候p->VirtualFunction,實際上是p->vfptr->VirtualFunction,它在構(gòu)造的時候就已經(jīng)指向了子類的VirtualFunction,所以調(diào)用的是子類的虛函數(shù),這就是多態(tài)了。

下面加上子類,并在子類中添加虛函數(shù),像下面這樣:

class DerivedClass: public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

可以看到子類內(nèi)存的排布如下:

上半部是內(nèi)存分布,可以看到,虛表指針被繼承了,且仍位于內(nèi)存排布的起始處,下面是父類的成員變量a和b,最后是子類的成員變量c,注意虛表指針只有一個,子類并沒有再生成虛表指針了;下半部的虛表情況與父類是一樣的。

我們把子類換個代碼,像這樣:

class DerivedClass1 : public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction2();
};

注意到這時我們并沒有覆寫父類的虛方法,而是重聲明了一個新的子類虛方法,內(nèi)存分布如下:

還是只有一個虛表指針,但是下方虛表的內(nèi)容變化了,虛表的0號是父類的VirtualFunction,而1號放的是子類的VirtualFunction2。也就是說,如果定義了DerivedClass的對象,那么在構(gòu)造時,虛表指針就會指向這個虛表,以后如果調(diào)用的是VirtualFunction,那么會從父類中尋找對應(yīng)的虛函數(shù),如果調(diào)用的是VirtualFunction2,那么會從子類中尋找對應(yīng)的虛函數(shù)。

我們再改造一下子類,像這樣:

class DerivedClass1 : public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
    void virtual VirtualFunction2();
};

我們既覆寫父類的虛函數(shù),也有新添的虛函數(shù),那么可以料想的到,是下面的這種內(nèi)存分布:

下面來討論多重繼承,代碼如下:

class Base
{
    int a;
    int b;
public:
    void CommonFunction();
    void virtual VirtualFunction();
};


class DerivedClass1: public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedClass2 : public Base
{
    int d;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void DerivedDerivedCommonFunction();
    void virtual VirtualFunction();
};

內(nèi)存分布從父類到子類,依次如下:

Base中有一個虛表指針,地址偏移為0

DerivedClass1繼承了Base,內(nèi)存排布是先父類后子類。

DerivedClass2的情況是類似于DerivedClass1的。

下面我們重點看看這個類DerivedDerivedClass,由外向內(nèi)看,它并列地排布著繼承而來的兩個父類DerivedClass1與DerivedClass2,還有自身的成員變量e。DerivedClass1包含了它的成員變量c,以及Base,Base有一個0地址偏移的虛表指針,然后是成員變量a和b;DerivedClass2的內(nèi)存排布類似于DerivedClass1,注意到DerivedClass2里面竟然也有一份Base。

這里有兩份虛表了,分別針對DerivedClass1與DerivedClass2,在&DerivedDericedClass_meta下方的數(shù)字是首地址偏移量,靠下面的虛表的那個-16表示指向這個虛表的虛指針的內(nèi)存偏移,這正是DerivedClass2中的{vfptr}在DerivedDerivedClass的內(nèi)存偏移。

如果采用虛繼承,像下面這樣:

class DerivedClass1: virtual public Base
{
    int c;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedClass2 : virtual public Base
{
    int d;
public:
    void DerivedCommonFunction();
    void virtual VirtualFunction();
};

class DerivedDerivedClass :  public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void DerivedDerivedCommonFunction();
    void virtual VirtualFunction();
};

Base類沒有變化,但往下看:

DerivedClass1就已經(jīng)有變化了,原來是先排虛表指針與Base成員變量,vfptr位于0地址偏移處;但現(xiàn)在有兩個虛表指針了,一個是vbptr,另一個是vfptr。vbptr是這個DerivedClass1對應(yīng)的虛表指針,它指向DerivedClass1的虛表vbtable,另一個vfptr是虛基類表對應(yīng)的虛指針,它指向vftable。

下面列出了兩張?zhí)摫?,第一張表是vbptr指向的表,8表示{vbptr}與{vfptr}的偏移;第二張表是vfptr指向的表,-8指明了這張表所對應(yīng)的虛指針位于內(nèi)存的偏移量。

DerivedClass2的內(nèi)存分布類似于DerivedClass1,同樣會有兩個虛指針,分別指向兩張?zhí)摫恚ǖ诙埵翘摶惐恚?/p>

下面來仔細(xì)看一下DerivedDerivedClass的內(nèi)存分布,這里面有三個虛指針了,但base卻只有一份。第一張?zhí)摫硎莾?nèi)含DerivedClass1的,20表示它的虛指針{vbptr}離虛基表指針{vfptr}的距離,第二張?zhí)摫硎莾?nèi)含DerivedClass2的,12表示它的虛指針{vbptr}離虛基表指針{vfptr}的距離,最后一張表是虛基表,-20指明了它對應(yīng)的虛指針{vfptr}在內(nèi)存中的偏移。

虛繼承的作用是減少了對基類的重復(fù),代價是增加了虛表指針的負(fù)擔(dān)(更多的虛表指針)。

下面總結(jié)一下(當(dāng)基類有虛函數(shù)時):

1. 每個類都有虛指針和虛表;

2. 如果不是虛繼承,那么子類將父類的虛指針繼承下來,并指向自身的虛表(發(fā)生在對象構(gòu)造時)。有多少個虛函數(shù),虛表里面的項就會有多少。多重繼承時,可能存在多個的基類虛表與虛指針;

3. 如果是虛繼承,那么子類會有兩份虛指針,一份指向自己的虛表,另一份指向虛基表,多重繼承時虛基表與虛基表指針有且只有一份。

以上就是解析C++類內(nèi)存分布的詳細(xì)內(nèi)容,更多關(guān)于C++類內(nèi)存分布的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++設(shè)計模式編程中使用Bridge橋接模式的完全攻略

    C++設(shè)計模式編程中使用Bridge橋接模式的完全攻略

    這篇文章主要介紹了C++設(shè)計模式編程中使用Bridge橋接模式的完全攻略,Bridge將抽象部分與它的實現(xiàn)部分分離,使它們都可以獨立地變化需要的朋友可以參考下
    2016-03-03
  • C語言利用sprintf固定字符串輸出位數(shù)

    C語言利用sprintf固定字符串輸出位數(shù)

    sprintf?函數(shù)是一個?C?語言中的函數(shù),也被許多其他編程語言所支持。這篇文章主要介紹了C語言如何利用sprintf固定字符串輸出位數(shù),需要的可以參考一下
    2023-03-03
  • 使用C++17實現(xiàn)JSON庫設(shè)計思路示例全解

    使用C++17實現(xiàn)JSON庫設(shè)計思路示例全解

    這篇文章主要為大家介紹了使用C++17實現(xiàn)JSON庫設(shè)計思路示例全解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Qt中線程常用通信方式介紹

    Qt中線程常用通信方式介紹

    Qt中,線程通信無處不在,最核心的特性信號槽就是一種線程間通信,這篇文章主要為大家介紹了幾種常用的方式,需要的小伙伴可以參考一下
    2025-01-01
  • C語言單值二叉樹真題講解

    C語言單值二叉樹真題講解

    單值二叉樹你可能之前沒見過,如果二叉樹每個節(jié)點都具有相同的值,那么該二叉樹就是單值二叉樹,讓我們通過一個真題來深刻了解它吧
    2022-04-04
  • C++的繼承法則詳解

    C++的繼承法則詳解

    本文詳細(xì)介紹了C++中的繼承機制,包括繼承的概念、定義、使用方法、訪問限定符、賦值兼容轉(zhuǎn)換、作用域、默認(rèn)成員函數(shù)、友元關(guān)系、靜態(tài)成員以及單繼承、多繼承和菱形繼承,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • C++?string如何獲取文件路徑文件名、文件路徑、文件后綴(兩種方式)

    C++?string如何獲取文件路徑文件名、文件路徑、文件后綴(兩種方式)

    這篇文章主要介紹了C++?string如何獲取文件路徑文件名、文件路徑、文件后綴(兩種方式),具有很好的參考價值,希望對大家有所幫助。
    2023-06-06
  • 講解C++中的枚舉類型以及聲明新類型的方法

    講解C++中的枚舉類型以及聲明新類型的方法

    這篇文章主要介紹了講解C++中的枚舉類型以及聲明新類型的方法,是C預(yù)言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-09-09
  • C++ std::unique_lock 用法實例詳解

    C++ std::unique_lock 用法實例詳解

    std::unique_lock 是 C++11 提供的一個用于管理互斥鎖的類,它提供了更靈活的鎖管理功能,適用于各種多線程場景,這篇文章給大家介紹了C++ std::unique_lock 用法,感興趣的朋友跟隨小編一起看看吧
    2023-09-09
  • Windows下sentry接入C/C++程序的詳細(xì)過程

    Windows下sentry接入C/C++程序的詳細(xì)過程

    sentry作為一個開源的軟件,發(fā)展至今,已經(jīng)非常成熟。它支持的平臺眾多,甚至于針對不同的工作者(后臺、前端、客戶端)都有相應(yīng)的內(nèi)容,這篇文章主要介紹了Windows下sentry接入C/C++程序,需要的朋友可以參考下
    2022-09-09

最新評論

贵溪市| 合川市| 政和县| 舞阳县| 康乐县| 闸北区| 延长县| 修武县| 家居| 潼关县| 南昌县| 闽清县| 平舆县| 铜梁县| 绥芬河市| 启东市| 临夏县| 大竹县| 吴桥县| 贵定县| 化德县| 焦作市| 丰顺县| 阜新市| 商南县| 铅山县| 祁门县| 贵阳市| 锡林郭勒盟| 洛川县| 三台县| 西华县| 江口县| 扎赉特旗| 横峰县| 丰原市| 博罗县| 西和县| 祁东县| 潢川县| 廉江市|