激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - 詳談C++中虛基類在派生類中的內存布局

詳談C++中虛基類在派生類中的內存布局

2021-04-23 13:31C++教程網 C/C++

下面小編就為大家帶來一篇詳談C++中虛基類在派生類中的內存布局。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

今天重溫C++的知識,當看到虛基類這點的時候,那時候也沒有太過追究,就是知道虛基類是消除了類繼承之間的二義性問題而已,可是很是好奇,它是怎么消除的,內存布局是怎么分配的呢?于是就深入研究了一下,具體的原理如下所示:

在C++中,obj是一個類的對象,p是指向obj的指針,該類里面有個數據成員mem,請問obj.mem和p->mem在實現和效率上有什么不同。

答案是:只有一種情況下才有重大差異,該情況必須滿足以下3個條件:

(1)、obj 是一個虛擬繼承的派生類的對象

(2)、mem是從虛擬基類派生下來的成員

(3)、p是基類類型的指針

當這種情況下,p->mem會比obj.mem多了兩個中間層。(也就是說在這種情況下,p->mem比obj.mem要明顯的慢)

WHY?

如果好奇心比較重的話,請往下看 :

1、虛基類的使用,和為多態而實現的虛函數不同,是為了解決多重繼承的二義性問題。

舉例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A
{
public:
  int a;
};
class B : virtual public A
{
public:
  int b;
};
class C :virtual public A
{
public:
  int c;
};
class D : public B, public C
{
public:
  int d;
};

上面這種菱形的繼承體系中,如果沒有virtual繼承,那么D中就有兩個A的成員int a;繼承下來,使用的時候,就會有很多二義性。而加了virtual繼承,在D中就只有A的成員int a;的一份拷貝,該拷貝不是來自B,也不是來自C,而是一份單獨的拷貝,那么,編譯器是怎么實現的呢??

在回答這個問題之前,先想一下,sizeof(A),sizeof(B),sizeof(C),sizeof(D)是多少?(在32位x86的linux2.6下面,或者在vc2005下面)在linux2.6下面,結果如下:sizeof(A) = 4; sizeof(B) = 12; sizeof(C) = 12; sizeof(D) = 24;sizeof(B)為什么是12呢,那是因為多了一個指針(這一點和虛函數的實現一樣),那個指針是干嘛的呢?

那么sizeof(D)為什么是24呢?那是因為除了繼承B中的b,C中的c,A中的a,和D自己的成員d之外,還繼承了B,C多出來的2個指針(B和C分別有一個)。再強調一遍,D中的int a不是來自B也不是來自C,而是另外的一份從A直接靠過來的成員。

如果聲明了D的對象d: D d;

那么d的內存布局如下:

vb_ptr: 繼承自B的指針
int b: 繼承自B公有成員
vc_ptr:繼承自C的指針
int c: 繼承自C的共有成員
int d: D自己的公有成員
int a: 繼承自A的公有成員
 
那么以下的用法會發生什么事呢?

?
1
2
3
D dD;
B *pb = &dD;
pb->a;

上面說過,dD中的int a不是繼承自B的,也不是繼承自C的,那么這個B中的pb->a又會怎么知道指向的是dD內存中的第六項呢?

那就是指針vb_ptr的妙用了。原理如下:(其實g++3.4.3的實現更加復雜,我不知道是出于什么考慮,而我這里只說原理,所以把過程和內容簡單化了)

首先,vb_ptr指向一個整數的地址,里面放的整數是那個int a的距離dD開始處的位移(在這里vb_ptr指向的地址里面放的是20,以字節為單位)。編譯器是這樣做的:

首先,找到vb_ptr(這個不用找,因為在g++中,vb_ptr就是B*中的第一項,呵呵),然后取得vb_ptr指向的地址的內容(這個例子是20),最后把這個內容與指針pb相加,就得到pb->a的地址了。

所以說這種時候,用指針轉換多了兩個中間層才能找到基類的成員,而且是運行期間。

由此也可以推知dD中的vb_ptr和vc_ptr的內容都是一樣的,都是指向同一個地址,該地址就放20(在本例中)

如下的語句呢:

?
1
2
A *pa = &dD;
pa->a = 4;

這個語句不用轉換了,因為編譯器在編譯期間就知道他把A中的成員插在dD中的那個地方了(在本例中是末尾),所以這個語句中的運行效率和dD.a是一樣的(至少也是差不多的)

這就是虛基類實現的基本原理。

注意的是:那些指針的位置和基類成員在派生類成員中的內存布局是不確定的,也就是說標準里面沒有規定inta必須要放在最后,只不過g++編譯器的實現而已。c++標準大概只規定了這套機制的原理,至于具體的實現,比如各成員的排放順序和優化,由各個編譯器廠商自己定~

非虛擬繼承:

在派生類對象里,按照繼承聲明順序依次分布基類對象,最后是派生類數據成員。

若基類聲明了虛函數,則基類對象頭部有一個虛函數表指針,然后是基類數據成員。

在基類虛函數表中,依次是基類的虛函數,若某個函數被派生類override,則替換為派生類的函數。

派生類獨有的虛函數被加在第一個基類的虛函數表后面。

虛擬繼承:

在派生類對象里,按照繼承聲明順序依次分布非虛基類對象,然后是派生類數據成員,最后是虛基類對象。

若基類聲明了虛函數,則基類對象頭部有一個虛函數表指針,然后是基類數據成員。

在基類虛函數表中,依次是基類的虛函數,若某個函數被派生類override,則替換為派生類的函數。

若直接從虛基類派生的類沒有非虛父類,且聲明了新的虛函數,則該派生類有自己的虛函數表,在該派生類頭部;否則派生類獨有的虛函數被加在第一個非虛基類的虛函數表后面。

直接從虛基類派生的類內部還有一個虛基類表指針(一個隱藏的“虛基類表指針”成員,指向一個虛基類表),在數據成員之前,非虛基類對象之后(若有的話)。

虛基類表中第一個值是該虛基類表到派生類起始地址的偏移;之后的值依次是該派生類的虛基類到該表位置的地址偏移(虛基類對象的地址與派生類的“虛基類表指針”之間的偏移量)。

對于虛函數表指針和虛基類表指針:

當單繼承且非虛繼承時:每個含有虛函數的表只有一個虛函數表,所以只需要一個虛表指針即可;

當多繼承且非虛繼承時:一個子類有幾個父類則會有幾個虛函數表,所以就有和父類個數相同的虛表指針來標識;

總之,當時非虛繼承時,不需要額外增加虛函數表指針。

當虛繼承時:無論是單虛繼承還是多虛繼承,需要有一個虛基類表來記錄虛繼承關系,所以此時子類需要多一個虛基類表指針;而且只需要一個即可。

當虛繼承時可能出現一個類中持有多個虛函數表的情況:無論是單虛繼承還是多虛繼承,

如果子類沒有構造函數和析構函數,且子類中的虛函數都是在父類中出現的虛函數,這個時候不需要增加任何虛表指針;只需要像多繼承那個持有父類個數的虛表指針來標識即可;

如果子類中含有構造函數或者析構函數或二者都有,則在子類中只要每出現一個父類中的虛函數則需要增加一個虛函數表指針來標識此類的虛函數表;

無論是否含有構造函數或者虛構函數,只要繼承都是虛繼承且出現了父類中沒有出現的虛函數,則在子類中需要再增加一個徐函數表指針;如果其中有一個是非虛繼承,則按照最省空間的原則,不需要增加虛函數表指針,因為這個時候可以和非虛基類共享一個虛函數表指針。

以上就是小編為大家帶來的詳談C++中虛基類在派生類中的內存布局全部內容了,希望大家多多支持服務器之家~

延伸 · 閱讀

精彩推薦
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 久久久久久久久久91 | 久久激情小视频 | 午夜精品成人一区二区 | 黄污免费网站 | 91av在线免费观看 | 男女羞羞视频 | www.guochan | 国产免费传媒av片在线 | 国产精品成aⅴ人片在线观看 | www.91成人 | 羞羞答答xxdd在线播放 | 韩国一级免费视频 | 成人性生活视频 | 国产正在播放 | 91系列在线观看 | www.av88| 欧美天堂一区 | chengrenzaixian| 美女性感毛片 | 99久久久国产精品免费99 | 久久最新网址 | 亚洲免费片 | 精品影视一区二区 | 亚洲欧洲日韩av | 成人免费淫片视频软件 | 神秘电影91| 久色免费 | 免费观看一区二区三区视频 | 国产精品久久久久久久久久10秀 | 九一国产精品 | 日产精品久久久一区二区福利 | 中文字幕在线看第二 | 成人免费网站在线观看视频 | 精品国产一区二区三区免费 | 高清国产福利 | 欧美性色黄大片www 成人免费网站在线观看 | 日本在线播放一区二区 | 一区二区三区无码高清视频 | 亚洲视屏 | 日本在线观看一区二区 | 欧美黄色性视频 |