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

歡迎訪問 生活随笔!

生活随笔

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

c/c++

【C++拾遗】 从内存布局看C++虚继承的实现原理

發布時間:2025/3/18 c/c++ 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C++拾遗】 从内存布局看C++虚继承的实现原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

原創作品,轉載請標明:http://blog.csdn.net/xiejingfa/article/details/48028491

準備工作

1、VS2012使用命令行選項查看對象的內存布局

微軟的Visual Studio提供給用戶顯示C++對象在內存中的布局的選項:/d1reportSingleClassLayout。使用方法很簡單,直接在[工具(T)]選項下找到“Visual Studio命令提示(C)”后點擊即可。切換到cpp文件所在目錄下輸入如下的命令即可

??????c1 [filename].cpp /d1reportSingleClassLayout[className]

其中[filename].cpp就是我們想要查看的class所在的cpp文件,[className]指我們想要查看的class的類名。(下面舉例說明...)

2、查看普通多繼承子類的內存布局

既然我們今天講的是虛基類和虛繼承,我們就先用上面介紹的命令提示工具查看一下普通多繼承子類的內存布局,可以跟后文虛繼承子類的內存布局情況加以比較。

我們新建一個名叫NormalInheritance的cpp文件,輸入一下內容。


/**
?? ?普通繼承(沒有使用虛基類)
*/
?
// 基類A
class A
{
public:
?? ?int dataA;
};
?
class B : public A
{
public:
?? ?int dataB;
};
?
class C : public A
{
public:
?? ?int dataC;
};
?
class D : public B, public C
{
public:
?? ?int dataD;
};

上面是一個簡單的多繼承例子,我們啟動Visual Studio命令提示功能,切換到NormalInheritance.cpp文件所在目錄,輸入一下命令:
c1 ?NormalInheritance.cpp /d1reportSingleClassLayoutD

我們可以看到class D的內存布局如下:

從類D的內存布局可以看到A派生出B和C,B和C中分別包含A的成員。再由B和C派生出D,此時D包含了B和C的成員。這樣D中就總共出現了2個A成員。大家注意到左邊的幾個數字,這幾個數字表明了D中各成員在D中排列的起始地址,D中的五個成員變量(B::dataA、dataB、C::dataA、dataC、dataD)各占用4個字節,sizeof(D) = 20。

為了跟后文加以比較,我們再來看看B和C的內存布局:

??????????????????????????????????

虛繼承的內存分布情況

上面我們看到了普通多繼承子類的內存分布情況,下面我們進入主題,來看看典型的菱形虛繼承子類的內存分布情況。

我們新建一個名叫VirtualInheritance的cpp文件,輸入一下內容:


/**
?? ?虛繼承(虛基類)
*/
?
#include <iostream>
?
// 基類A
class A
{
public:
?? ?int dataA;
};
?
class B : virtual public A
{
public:
?? ?int dataB;
};
?
class C : virtual public A
{
public:
?? ?int dataC;
};
?
class D : public B, public C
{
public:
?? ?int dataD;
};

VirtualInheritance.cpp和NormalInheritance.cpp的不同點在與C和C繼承A時使用了virtual關鍵字,也就是虛繼承。同樣,我們看看B、C、D類的內存布局情況:


????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

????????????????????????????????????

我們可以看到,菱形繼承體系中的子類在內存布局上和普通多繼承體系中的子類類有很大的不一樣。對于類B和C,sizeof的值變成了12,除了包含類A的成員變量dataA外還多了一個指針vbptr,類D除了繼承B、C各自的成員變量dataB、dataA和自己的成員變量外,還有兩個分別屬于B、C的指針。

那么類D對象的內存布局就變成如下的樣子:

vbptr:繼承自父類B中的指針

int dataB:繼承自父類B的成員變量

vbptr:繼承自父類C的指針

int dataC:繼承自父類C的成員變量

int dataD:D自己的成員變量

int A:繼承自父類A的成員變量

顯然,虛繼承之所以能夠實現在多重派生子類中只保存一份共有基類的拷貝,關鍵在于vbptr指針。那vbptr到底指的是什么?又是如何實現虛繼承的呢?其實上面的類D內存布局圖中已經給出答案:

?????????????????????????????????????????????????????????????

實際上,vbptr指的是虛基類表指針(virtual base table pointer),該指針指向了一個虛表(virtual table),虛表中記錄了vbptr與本類的偏移地址;第二項是vbptr到共有基類元素之間的偏移量。在這個例子中,類B中的vbptr指向了虛表D::$vbtable@B@,虛表表明公共基類A的成員變量dataA距離類B開始處的位移為20,這樣就找到了成員變量dataA,而虛繼承也不用像普通多繼承那樣維持著公共基類的兩份同樣的拷貝,節省了存儲空間。

為了進一步確定上面的想法是否正確,我們可以寫一個簡單的程序加以驗證:


int main()
{
?? ?D* d = new D;
?? ?d->dataA = 10;
?? ?d->dataB = 100;
?? ?d->dataC = 1000;
?? ?d->dataD = 10000;
?
?? ?B* b = d; // 轉化為基類B
?? ?C* c = d; // 轉化為基類C
?? ?A* fromB = (B*) d;
?? ?A* fromC = (C*) d;
?
?? ?std::cout << "d address ? ?: " << d << std::endl;
?? ?std::cout << "b address ? ?: " << b << std::endl;
?? ?std::cout << "c address ? ?: " << c << std::endl;
?? ?std::cout << "fromB address: " << fromB << std::endl;
?? ?std::cout << "fromC address: " << fromC << std::endl;
?? ?std::cout << std::endl;
?
?? ?std::cout << "vbptr address: " << (int*)d << std::endl;
?? ?std::cout << " ? ?[0] => " << *(int*)(*(int*)d) << std::endl;
?? ?std::cout << " ? ?[1] => " << *(((int*)(*(int*)d)) + 1)<< std::endl; // 偏移量20
?? ?std::cout << "dataB value ?: " << *((int*)d + 1) << std::endl;
?? ?std::cout << "vbptr address: " << ((int*)d + 2) << std::endl;
?? ?std::cout << " ? ?[0] => " << *(int*)(*((int*)d + 2)) << std::endl;
?? ?std::cout << " ? ?[1] => " << *((int*)(*((int*)d + 2)) + 1) << std::endl; // 偏移量12
?? ?std::cout << "dataC value ?: " << *((int*)d + 3) << std::endl;
?? ?std::cout << "dataD value ?: " << *((int*)d + 4) << std::endl;
?? ?std::cout << "dataA value ?: " << *((int*)d + 5) << std::endl;
}

得到結果為:


??????????????????????????????????????????????
---------------------?
作者:Fred^_^?
來源:CSDN?
原文:https://blog.csdn.net/Xiejingfa/article/details/48028491?
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

轉載于:https://my.oschina.net/u/4000302/blog/3022193

總結

以上是生活随笔為你收集整理的【C++拾遗】 从内存布局看C++虚继承的实现原理的全部內容,希望文章能夠幫你解決所遇到的問題。

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