c++虛函數表在運行時候是如何存在的?

#include &

class A {
public:
A() {};
virtual ~A() {};
virtual void echo() { std::cout &<&< "A::echo" &<&< std::endl; }; }; class B :public A { public: B() {}; ~B() {}; virtual void echo()override { std::cout &<&< "B::echo" &<&< std::endl; }; virtual void print() { std::cout &<&< "B::print" &<&< std::endl; }; }; #ifndef _WIN64 typedef unsigned int pointer;//32位指針 #else typedef unsigned long long pointer;//64位指針 #endif int main() { B b; B* b1 = new B(); A* a = new A(); A *a1 = dynamic_cast &(b1);
B* b2 = dynamic_cast&(a1);

pointer vfptr_b = *(pointer*)b;
pointer vfptr_b1 = *(pointer*)b1;

pointer vfptr_a = *(pointer*)a;
pointer vfptr_a1 = *(pointer*)a1;
pointer vfptr_b2 = *(pointer*)b2;

std::cout
&<&< vfptr_b &<&< " " &<&< vfptr_b1 &<&< " " &<&< vfptr_a &<&< " " &<&< vfptr_a1 &<&< " " &<&< vfptr_b2 &<&< std::endl; return 0; }

如上代碼,將對象地址強制轉換為指針地址,得到vftable的地址,其中除了vfptr_a,其餘對象的虛函數表的地址是一樣的,說明這些對象共用了一個虛函數表。

我的問題如下:

是不是所有帶有虛函數表的對象,都有一個固定的虛函數表(我看到調用虛函數的反彙編代碼,虛函數在表裡的偏移量是定死的!)?

如果new的是子類,轉換為父類指針,父類對象指針帶的虛函數表在使用時候,是不是只使用前一段?


主流 C++ 實現(gcc/clang/VC++)對於最簡單的單繼承且基類有虛函數的情況(非虛擬繼承)的做法都差不多:

  • vtable 是每個 class 類型一個,不是每個對象一個。vtable 的大小(長度)跟這個 class 的虛函數總數(繼承和自己新增)正相關。

  • vptr 是每個對象一個,vptr 大小是固定的,跟虛函數多少無關。

  • 對象的 vptr 在構造和析構的時候可能會變,指向不同的 vtable。對象構造完就不會變了。

  • 「只使用前一段」有可能發生,如果派生類添加了新的虛函數的話(比如 virtual void B::print())。

以上說法當然很不完善,每句話都有漏洞,請語言律師不必糾纏我。


首先強調一點,虛函數表不是C++語言的,是C++語言的實現的。C++沒規定虛函數要怎麼實現,所以用不用虛函數表,虛函數表究竟怎麼個邏輯並沒有一定之規。

虛函數會在虛函數表裡有一個可以確定的位置,可以粗略說成在表裡有一個固定的位置。否則就沒辦法定位了。

我不太確定你說固定的虛函數表是怎麼回事。一般的做法,除非子類原樣使用父類的虛函數,不加也不改,否則都會有自己的虛函數表。

父類的虛函數會佔用子類虛函數表確定的一部分。單繼承的話,對於一般的實現來說會是前一部分。

另外,虛函數表指針是存對象里的,不是存指針里的。


A的析構函數,方便的話加上virtual


推薦閱讀:

cocos2dx開發遊戲,如果可以使用C++做出來,是不是不用lua,全用C++做更好呢?
C++中this指針藏在哪裡?
一個類有幾個this指針?如果只有一個,那是怎麼區分不同的對象呢?
C++中的struct?
如何快速入門UE4開發?

TAG:C | VisualC | 編譯 | g | 虛函數C |