C 虛函式實現機制

2021-09-11 18:44:43 字數 2914 閱讀 9407

c++程式的記憶體格局通常分為四個區:全域性資料區,**區,棧區,和堆區(即自由儲存區)。

全域性資料區存放全域性變數,靜態資料和常量;所有類成員函式和非成員函式**存放在**區;為執行函式而分配的區域性變數,函式引數,返回資料,返回位址等存放在棧區,餘下的空間都被成為堆區。

類成員函式是放在**區,而類的靜態成員變數在類定義時就已經在全域性資料區分配了記憶體,因而它是屬於類的。對於非靜態成員變數,我們是在類的例項化過程中(構造物件)才在棧區或者堆區為其分配記憶體,是為每個對相關生成乙個拷貝,所以它是屬於物件的。

靜態成員函式和非靜態成員函式都是在類的定義時放在記憶體的**區的,因而可以說它們都是屬於類的。區別在於類的非靜態類成員函式其實都內含了乙個指向類物件的指標引數(即this指標),因而只有類物件才能呼叫(此時this指標有實值)。

所有說類的成員函式其實是有大小的,其大小為void的兩倍。在64位的機器下,void的大小為8個位元組,類成員函式的大小為16個位元組,猜想是因為裡面還存了一根this指標。

#include class itype 

};class itype2

};class myclass : itype, itype2

};int main()

要想知道c++物件的記憶體布局, 可以有多種方式, 比如:

輸出成員變數的偏移, 通過offsetof巨集來得到

通過偵錯程式檢視, 比如常用的vs

base1_1前面多了乙個變數__vfptr(常說的虛函式表vtable指標),其型別為void**,這說明它是乙個void*指標,它是乙個指向指標陣列的指標。

虛函式指標__vfptr位於所有的成員變數之前定義。

class base1

virtual void base1_fun2() {}

};

我們再加上乙個虛函式。

我們發現類的大小沒變,這更加堅定了我們認為__vfptr是乙個指向陣列的指標。

而__vfptr所指向的函式指標陣列裡出現了第2個元素,其值為base2類的第2個虛函式base1_fun2()函式的位址。

void* __fun = ;

const void** __vfptr = &__fun[0];

更加堅定我們前面描述的:__vfptr只是乙個指向函式指標陣列的指標。

增加乙個虛函式,只是簡單地向該類所對應的虛函式表裡增加一項而已,並不會影響到類物件的大小以及布局情況。

同乙個類的不同例項共用乙份虛函式表,它們都通過乙個所謂的虛函式指標__vfptr指向該虛函式表。

虛函式表再編譯時期為我們建立好的,只存在乙份。定義類物件時,編譯器自動將類物件的__vfptr指向這個虛函式表。

};// 多繼承

class derive1 : public base1, public base2

virtual void base2_fun2() {}

// 自身定義的虛函式

可見derive1裡面既有base1類的虛函式指標,又有base2類的虛函式指標,所以sizeof才會是40.

而derive1自身的虛函式其實在base1類的虛函式表裡(vs debug模式裡看不到,從彙編中可以看到),如果base1類裡沒有虛函式,那麼就在base2類裡,且base2類的位置更前。

C 虛函式實現機制

看完之後,對c 中的虛函式實現機制算的上是恍然大悟,但是個人感覺博文中有幾點不足之處,現在一一枚舉,以下語言僅僅代表個人看法 1.定位虛表的方式 大家都知道含有虛函式的類的例項裡面前4個位元組是虛函式指標占用的記憶體,裡面填充的是虛函式表的位址號。原博文中通過乙個long型別的物件取得前四個位元組的...

c 虛函式實現機制

1 c 實現多型的方法 其實很多人都知道,虛函式在c 中的實現機制就是用虛表和虛指標,但是具體是怎樣的呢?從more effecive c 其中一篇文章裡面可以知道 是每個類用了乙個虛表,每個類的物件用了乙個虛指標。具體的用法如下 class a class b public a a,b的實現省略 ...

C 虛函式實現機制

c 中的虛函式的作用主要是實現了多型的機制。關於多型,簡而言之就是用父型別別的指標指向其子類的例項,然後通過父類的指標呼叫實際子類的成員函式。對c 了解的人都應該知道虛函式 virtual function 是通過一張虛函式表 virtual table 來實現的。簡稱為v table。在這個表中,...