C 繼承多型下的記憶體分布

2021-06-08 11:05:59 字數 3083 閱讀 7890

(2012-05-08 23:36:58)

虛函式是物件導向程式設計語言裡乙個很重要的機制,下面我們以乙個c++例子,分析其對應的c語言程式來說明虛函式的機制。

物件導向有了乙個重要的概念就是物件的例項,物件的例項代表乙個具體的物件,故其肯定有乙個資料結構儲存這例項的資料,這一資料報括變數,介面函式指標,如果是虛函式,則有相應的虛函式指標,其他函式指標不包括。

要講虛函式機制,必須講繼承,因為只有繼承才有虛函式的動態繫結功能,先講下c++繼承物件例項記憶體分配基礎知識:

c++繼承分為兩種,普通繼承虛擬繼承(virtual)。具體的繼承又根據父類中的函式是否virtual而不同。

下面就單繼承分為幾種情況闡述:

1.普通繼承+父類無virtual函式

若子類沒有新定義virtual函式 此時子類的布局是 :

由低位址->高位址 為父類的元素(沒有vptr),子類的元素(沒有vptr).

若子類有新定義virtual函式 此時子類的布局是 :

由低位址->高位址 為父類的元素(沒有vptr),子類的元素(包含vptr,指向vtable.)

2. 普通繼承+父類有virtual函式

不管子類沒有新定義virtual函式 此時子類的布局是 : 由低位址->高位址 為父類的元素(包含vptr), 子類的元素.

如果子類有新定義的virtual函式,那麼在父類的vptr(也就是第乙個vptr)對應的vtable中新增乙個函式指標.

3.virtual繼承

若子類沒有新定義virtual函式 此時子類的布局是 :

由低位址->高位址 子類的元素(有vptr),虛基類的元素.為什麼這裡會出現vptr,因為虛基類派生出來的類中,虛類的物件不在固定位置(猜測應該是在記憶體的尾部),需要乙個中介才能訪問虛類的物件.所以雖然沒有virtual函式,子類也需要有乙個vptr,對應的vtable中需要有一項指向虛基類.

若子類有新定義virtual函式 此時子類的布局是與沒有定義新virtual函式記憶體布局一致.但是在vtable中會多出新增的虛函式的指標.

4.多重繼承

此時子類的布局是 :

由低位址->高位址 為父類p1的元素(p1按照實際情況確定元素中是否包含vptr), 父類p2的元素(p2按照實際情況確定元素中是否包含vptr),子類的元素.

如果所有父類都沒有vptr,那麼如果子類定義了新的virtual function,那麼子類的元素中會有vptr,對應的vtable會有相應的函式指標.

如果有的父類存在vptr.如果子類定義了新的virtual function,會生成乙個子類的vtable,這個子類的vtable是,在它的父類的vtable中後新增這個新的虛函式指標生成的.因為子類分配的空間顯示並沒有新增加乙個4位元組的指標空間,其實不管子類增加了多少新的虛函式,其空間大小不變,因為其和虛函式相關的分配的空間就是乙個vptr,是乙個指標,也就是4位元組,不變,要變是變在vtable.

fun1() {};

public virtual  a();

public virtual  b(int b);

int a;

}class test2 extends test1;

public virtual b(int b) ;

public  vitrual c()」)}

int b;

}int main()

首先我們看看下:類test1,test2例項大小及其記憶體分配圖:

test1的例項資料大小是:虛函式表指標(4)+iaptr介面指標+int變數大小(4)=12

而test2的例項資料大小是:test1大小+其變數b大小=12+4=16

注意這是上面的提到的虛類繼承,子類新增的虛函式不增加子類大小,只是在其虛函式表中體現。

大家注意上面的test1,test2的建構函式,析構函式,fun1,fun2都沒加進去。

下面看下例項資料記憶體分布圖: 

下面看下其對應的c語言偽**;

1.  已實現的函式:

test1.b(sturct test1 *this ,b);

test2.b(sturct test2 *this ,b)」)}

//上面是虛函式實現

test1.fun1(sturct test1 *this,){};

test2.fun2(sturct test2 *this){};

//這個是普通函式,就是上面的,只不過變了名字而已。

2.會生成類對應的結構體:

struct test1test1;

struct test2test2;

3.會生成兩個虛函式表結構體:

struct test1_vtbl

struct test1_vtbl test1_vtb1=;

//父類test1虛函式表。

struct test2_vtbl

struct test2_vtbl  test2_vtb1=;

//子類test2虛函式表。

//注意虛函式a還是父類的a,因為其沒有過載,而b過載了就是test2的b了,同時析構函式也是虛函式,是自動加的。

4.編譯器自動生成的一些函式,建構函式,析構函式: 

test1.test1(sturct test1 *this )

test1.~test1(sturct test2 *this);

test2.test2(sturct test2 *this)

test2.~test2(struct test2 *this);

5.main函式對應的**:

int main()

對應c偽**:

int main()

我們現在分析a->vtbl->b(a,1) 是如何呼叫到test2.b()函式的。

執行1後,虛函式表是空的,即為null;

執行2 

test1 a=new test2();

a.  fun1();

}其對應於fun1(tes1 this);

如果乙個x函式是虛函式,執行x才會呼叫this虛函式表中的x,如果是this.x,就直接繫結test.x函式了。

本篇文章**於:開發學院 

C 多型呼叫和繼承記憶體分布

以下面 為例 class a virtual void fa void faa class b virtual void fb class c public a void fa void faa void main 首先c c 建立乙個c型別的物件c,類c是繼承a的。在vs2012中除錯,在監視中檢...

C 物件繼承後的記憶體分布

1.如果父類的純虛函式沒有實現,在沒有使用的的情況下 沒有new 或者直接生成物件 編譯不會報未定義。最近將乙個類物件指標直接轉換為void 儲存到了vector中,使用時再用static cast轉換為對應的父類指標,發現在多繼承的情況下這樣會有問題。原因是此物件有多個父類,static cast...

C 多型 繼承多型

什麼是多型?個人理解為 在程式語言繼承關係中,子類能替代父類,表現出不同的行為。換句話說 在繼承關係中,乙個類被例項化被其子類替代,子類中有父類的虛方法重寫,或者有父類同名方法 new 呼叫相同方法時候,將表現出子類或者父類中不同行為 老闆,上 static void main string arg...