日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++ 多重继承之内存存储

發布時間:2023/12/20 c/c++ 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++ 多重继承之内存存储 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C++ 之多重繼承

1. C++中class與struct。

在C++里面,class與struct沒有本質的區別,只是class的默認權限是private,而struct則是public。這個概念也揭示了一點:class和struct在內部存儲結構上是一致的。所以我們可以利用這一點來探討class的實現原理。我們可以將class轉換成對應的struct對象,通過struct的簡單性來展示class的內存存儲結構。

2. 關于class的基本內存結構

class包括成員變量和成員函數。對于成員變量,其結構和struct的結構是一致的,即按照聲明的順序,安排每個成員的內存位置。對于成員函數,如果是非虛函數(包括普通函數和靜態函數),他們實際上同其他函數沒有區別。對于非靜態非虛函數,默認隱藏了this參數。當編譯時,編譯器將函數地址直接編譯進去。因此,這類函數沒有動態能力。對于虛函數,其函數地址將存在類this指針關聯的虛函數內(參見上一篇文章),在運行時,從虛函數表內取得地址后再調用。

這樣一個不帶虛函數的類的內存結構,等同與一個類似的struct,而帶虛函數的類的內存結構,等同于一個帶有虛函數指針的struct。我用偽代碼可以清晰的表示出來:

[cpp]?view plaincopy
  • class?ClassA?{??
  • ???int?a;??
  • ???flaot?b;??
  • ???void?*c;??
  • };??
  • //等同于??
  • struct?StructA?{??
  • ???int?a;??
  • ???float?b;??
  • ???void?*c;??
  • <span?style="font-family:Arial,?Helvetica,?sans-serif;">};??
  • ??
  • //帶虛函數的類</span><pre?name="code"?class="plain">offset?A::a=0,?offset?B::a=8,?offset?B::b=12??
  • b=0x7fffad613130,?pa=0x7fffad613138??

  • class ClassAWithVirtual { int a; float b; void* c; virtual ~ClassAWithVirtual();};//等同于struct StructAWithVirtual { VTable *vtable; //包括虛函數表的vtable int a; float b; void *c;};


    3. class的單線繼承

    單線繼承是很簡單也很容易理解的一種繼承方式。如果有class B繼承自class A。那么,在class B的低地址部分,是class A的成員空間。這樣class B可以直接轉換為class A。

    如果class A有虛函數,那么class B必須也有虛函數表。那么,如果class A沒有虛函數,而class B卻有虛函數,那么這個時候的內存分布應該是什么樣呢?class B還能否直接轉換為class A呢?

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stddef.h>??
  • ??
  • #define?OFFSET(X,m)?(offsetof(X,?m))??
  • ??
  • class?A?{??
  • public:??
  • ????int?a;??
  • };??
  • ??
  • class?B?:?public?A{??
  • public:??
  • ????int?b;??
  • ????virtual?~B()?{?}??
  • };??
  • ??
  • int?main()?{??
  • ????printf("offset?A::a=%d,?offset?B::a=%d,?offset?B::b=%d\n",?OFFSET(A,a),?OFFSET(B,?a),?OFFSET(B,?b));??
  • ??
  • ????B?b;??
  • ????A?*pa?=?&b;??
  • ????printf("b=%p,?pa=%p\n",?&b,?pa);??
  • ??
  • ????return?0;??
  • }??

  • 在G++ 4.6 ubuntu 10.04下,輸出的結果是

    [cpp]?view plaincopy
  • offset?A::a=0,?offset?B::a=8,?offset?B::b=12??
  • b=0x7fffad613130,?pa=0x7fffad613138??
  • 很明顯的可以看到,當B有虛函數時,B必須在內存的開始處添加一個8字節(64位系統)的虛函數表指針。這個時候,在class B內,A的成員變量不能從偏移為0的位置開始了。



    3. 不帶虛函數的C++的多重繼承類的內存分布

    一般情況下,如果一個類繼承自兩個類以上,那么,它的內存分布會像壘磚頭那樣一層一層的添加上去。比如

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stddef.h>??
  • ??
  • ??
  • class?A?{??
  • public:??
  • ????int?a;??
  • };??
  • ??
  • ??
  • class?B?{??
  • public:??
  • ????int?b;??
  • };??
  • ??
  • ??
  • class?C?:?public?A,?public?B{??
  • public:??
  • ????int?c;??
  • };??
  • ??
  • ??
  • int?main()?{??
  • ????printf("Offset:?C::a=%d,?C::b=%d,?C::c=%d\n",?offsetof(C,?a),?offsetof(C,?b),?offsetof(C,?c));??
  • ????C?c;??
  • ????A?*pa?=?&c;??
  • ????B?*pb?=?&c;??
  • ??
  • ??
  • ????printf("c=%p,?pa=%p,?pb=%p\n",?&c,?pa,?pb);??
  • ??????
  • ????return?0;??
  • };??

  • 輸出結果是

    [cpp]?view plaincopy
  • Offset:?C::a=0,?C::b=4,?C::c=8??
  • c=0x7fffc90bbab0,?pa=0x7fffc90bbab0,?pb=0x7fffc90bbab4??
  • 這個與預想的很相似,它的等價結構是

    [cpp]?view plaincopy
  • struct?C?{??
  • ???struct?A?a;??
  • ???struct?B?b;??
  • ???int?c;??
  • };??
  • 的確像磚頭一樣,先放A,在放B,最后C的成員放上去。


    那么,再考慮一種情況,如果是這樣一種繼承方式:

    ? ? ? ? ? ? ? ? ? ? ? ? ?A

    ? ? ? ? ? ? ? ? ? ? ? / ? ? \ ? ? ??

    ? ? ? ? ? ? ? ? ? ? B ? ? ?C

    ? ? ? ? ? ? ? ? ? ? ?\ ? ? ?/

    ? ? ? ? ? ? ? ? ? ? ? ?D

    那么A的成員在D內部是一份還是兩份?

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stddef.h>??
  • ??
  • class?A?{??
  • public:??
  • ????int?a;??
  • };??
  • ??
  • class?B?:?public?A{??
  • public:??
  • ????int?b;??
  • };??
  • ??
  • class?C?:?public?A?{??
  • public:??
  • ????int?c;??
  • };??
  • ??
  • class?D?:?public?B,?public?C?{??
  • public:??
  • ???int?d;??
  • };??
  • ??
  • int?main()?{??
  • ????printf("Offset:?D::a=%d,?D::b=%d,?D::c=%d,?D::d\n",?offsetof(D,?B::a),?offsetof(D,?b),?offsetof(D,?c),?offsetof(D,?d));??
  • ????D?d;??
  • ????B?*pb?=?&d;??
  • ????C?*pc?=?&d;??
  • ????A?*pa_b?=?(A*)pb;??
  • ????A?*pa_c?=?(A*)pc;??
  • ??
  • ????printf("d=%p,?pa_b=%p,?pa_c=%p,?pb=%p,?pc=%p,?d.B::a=%p,?d.C::a=%p\n",?&d,?pa_b,?pa_c,?pb,?pc,?&d.B::a,?&d.C::a);??
  • ??????
  • ????return?0;??
  • };??
  • 先看一下輸出結果:

    [cpp]?view plaincopy
  • Offset:?D::a=0,?D::b=4,?D::c=12,?D::d??
  • d=0x7fffad730f30,?pa_b=0x7fffad730f30,?pa_c=0x7fffad730f38,?pb=0x7fffad730f30,?pc=0x7fffad730f38,?d.B::a=0x7fffad730f30,?d.C::a=0x7fffad730f38??
  • 事實上,如果我直接寫b.a是錯誤的,因為編譯器不知到應該選擇那個a。同樣的,如果寫A *pa = &d也是錯誤的。

    結合輸出結果,class D內部仍然等同于

    [cpp]?view plaincopy
  • struct?D?{??
  • ???struct?B?b;??
  • ???struct?C?c;??
  • ???int?d;??
  • };??
  • 而且存在兩份A,這兩份A分別包含在B和C內部。在使用時,必須正確指出是那個。
    ?

    由此可見,不管繼承層次有多深,C++總是按照這種壘磚頭的方式疊加。如果有祖先類內部有重復包含,那么,C++也會重復包含相同的內容。

    這也提醒我們,多重繼承不能太復雜,否則就很難搞清楚其結構關系了。


    4. 帶虛函數的類的多重繼承的內存分布

    帶虛函數的情況下,情況會變得非常復雜。首先,對于最簡單的一種繼承方式

    ? ? ? ? ? ? ? ? ? ? ? ? ?A ? ? ?B

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? \ ? /
    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?C


    我們需要分好幾種情況來考慮:

    ? ?1、A B虛 C非虛

    ? ?2、A B 非虛,C虛
    ? ?3、A B 其中一個虛,C虛

    ? ?4、A B C 都虛

    4.1 A B 虛 C非虛

    如果只有A虛, 按照默認的規則,A的內存會被安排在偏移0處。這個時候,A的虛函數表也就是C的虛函數表。

    如果只有B虛,因為B的內存會被安排在A之后,那么,B的虛函數表應該在B所在位置,C沒有虛函數表。


    4.2 A B 不虛,C虛

    這種情況下,虛函數表應該在偏移0處,然后才是A和B的內存結構。我們來驗證一下:


    總結

    以上是生活随笔為你收集整理的C++ 多重继承之内存存储的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。