欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++虛繼承的實(shí)現(xiàn)原理由內(nèi)存布局開始講起

 更新時(shí)間:2022年06月06日 08:59:59   作者:Fred^_^  
為了解決多繼承時(shí)的命名沖突和冗余數(shù)據(jù)問題,C++提出了虛繼承,使得在派生類中只保留一份間接基類的成員,下面我們從內(nèi)存布局看看虛繼承的實(shí)現(xiàn)原理

準(zhǔn)備工作

1、VS2012使用命令行選項(xiàng)查看對(duì)象的內(nèi)存布局

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

c1 [filename].cpp /d1reportSingleClassLayout[className]

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

2、查看普通多繼承子類的內(nèi)存布局

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

我們新建一個(gè)名叫NormalInheritance的cpp文件,輸入一下內(nèi)容。

/**
	普通繼承(沒有使用虛基類)
*/
// 基類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;
};

上面是一個(gè)簡單的多繼承例子,我們啟動(dòng)Visual Studio命令提示功能,切換到NormalInheritance.cpp文件所在目錄,輸入一下命令:

c1 NormalInheritance.cpp /d1reportSingleClassLayoutD

我們可以看到class D的內(nèi)存布局如下:

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

為了跟后文加以比較,我們?cè)賮砜纯碆和C的內(nèi)存布局:

虛繼承的內(nèi)存分布情況

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

我們新建一個(gè)名叫VirtualInheritance的cpp文件,輸入一下內(nèi)容:

/**
	虛繼承(虛基類)
*/
#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的不同點(diǎn)在與C和C繼承A時(shí)使用了virtual關(guān)鍵字,也就是虛繼承。同樣,我們看看B、C、D類的內(nèi)存布局情況:

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

那么類D對(duì)象的內(nèi)存布局就變成如下的樣子:

  • vbptr:繼承自父類B中的指針
  • int dataB:繼承自父類B的成員變量
  • vbptr:繼承自父類C的指針
  • int dataC:繼承自父類C的成員變量
  • int dataD:D自己的成員變量
  • int A:繼承自父類A的成員變量

顯然,虛繼承之所以能夠?qū)崿F(xiàn)在多重派生子類中只保存一份共有基類的拷貝,關(guān)鍵在于vbptr指針。那vbptr到底指的是什么?又是如何實(shí)現(xiàn)虛繼承的呢?其實(shí)上面的類D內(nèi)存布局圖中已經(jīng)給出答案:

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

為了進(jìn)一步確定上面的想法是否正確,我們可以寫一個(gè)簡單的程序加以驗(yàn)證:

int main()
{
	D* d = new D;
	d->dataA = 10;
	d->dataB = 100;
	d->dataC = 1000;
	d->dataD = 10000;
	B* b = d; // 轉(zhuǎn)化為基類B
	C* c = d; // 轉(zhuǎn)化為基類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;
}

得到結(jié)果為:

到此這篇關(guān)于C++虛繼承的實(shí)現(xiàn)原理由內(nèi)存布局開始講起的文章就介紹到這了,更多相關(guān)C++虛繼承內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++調(diào)試記錄與心得分享

    C++調(diào)試記錄與心得分享

    本文給大家詳細(xì)記錄了一次C++的調(diào)試過程,以及調(diào)試的心得,非常的實(shí)用,有需要的小伙伴可以參考下
    2017-07-07
  • C++中對(duì)象的常引用總結(jié)

    C++中對(duì)象的常引用總結(jié)

    以下是對(duì)C++中對(duì)象的常引用進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助
    2013-10-10
  • 深入講解C++中的構(gòu)造函數(shù)

    深入講解C++中的構(gòu)造函數(shù)

    這篇文章主要介紹了C++中的構(gòu)造函數(shù),是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • C語言中初始、增加和刪除進(jìn)程信號(hào)的操作方法簡介

    C語言中初始、增加和刪除進(jìn)程信號(hào)的操作方法簡介

    這篇文章主要介紹了C語言中初始、增加和刪除進(jìn)程信號(hào)的操作方法簡介,分別是sigemptyset函數(shù)、sigaddset函數(shù)和sigdelset函數(shù)的用法,需要的朋友可以參考下
    2015-09-09
  • C++中函數(shù)匹配機(jī)制詳解

    C++中函數(shù)匹配機(jī)制詳解

    大家好,本篇文章主要講的是C++中函數(shù)匹配機(jī)制詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-02-02
  • C語言中回調(diào)函數(shù)的含義與使用場景詳解(2)

    C語言中回調(diào)函數(shù)的含義與使用場景詳解(2)

    這篇文章主要為大家詳細(xì)介紹了C語言中回調(diào)函數(shù)的含義與使用場景,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • C++為什么不能修改set里的值?非要修改怎么辦?

    C++為什么不能修改set里的值?非要修改怎么辦?

    因?yàn)橹暗奈恼掠姓f過C++中 set的介紹及用法,今天這篇文章我們就來說說C++為什么不能修改set里的值,如果非要修改的話應(yīng)該怎么辦,下面我們一起進(jìn)入文章看看下面內(nèi)容,需要的朋友可以參考以下,希望對(duì)你有所幫助
    2021-11-11
  • C++執(zhí)行Linux Bash命令的方法

    C++執(zhí)行Linux Bash命令的方法

    今天小編就為大家分享一篇C++執(zhí)行Linux Bash命令的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2019-07-07
  • Linux網(wǎng)絡(luò)編程之UDP Socket程序示例

    Linux網(wǎng)絡(luò)編程之UDP Socket程序示例

    這篇文章主要介紹了Linux網(wǎng)絡(luò)編程之UDP Socket程序示例,有助于讀者在實(shí)踐中掌握UDP協(xié)議的原理及應(yīng)用方法,需要的朋友可以參考下
    2014-08-08
  • 詳解C++?中?shared_ptr?weak_ptr

    詳解C++?中?shared_ptr?weak_ptr

    shared_ptr?是一個(gè)標(biāo)準(zhǔn)的共享所有權(quán)的智能指針,允許多個(gè)指針指向同一個(gè)對(duì)象,定義在?memory?文件中,命名空間為?std,這篇文章主要介紹了C++?中?shared_ptr?weak_ptr,需要的朋友可以參考下
    2022-07-07

最新評(píng)論