明確以下幾點:
多態的實現是依靠虛函數表,程序需要額外的查詢虛函數表的開銷。
C++的構造函數中可以調用虛函數,說明虛函數表的產生是在構造函數調用之前。虛函數表的建立是在編譯時(Compile-Time)。
看一個例子:
#include <iostream> using namespace std; class A { int m1; int m2; public: A() { fun2(); } virtual void fun2() { cout<<"call A::fun2 success!"<<endl; } }; class B:public A { public: B() { fun2(); } void fun2() { cout<<"call B::fun2 success!"<<endl; } }; int main() { B *pa = new B(); return 1; }
輸出結果:
call A::fun2 success!
call B::fun2 success!
說明兩個構造函數內的虛函數都調用成功。由於對象的構造順序和子類對基類虛函數的重載還未完成,所以第一個fun函數是調用的A的虛函數。
(在JAVA中會輸出兩個call B::fun2 success!,WHY??)
虛函數表的地址vptr是存在對象的this指針指向的結構體的第一個數據位置,即存儲vptr的地址和this的地址值相同。
一個類的所有對象都共享同一份虛函數表。
看一個例子:
#include <iostream> using namespace std; class A { int m1; int m2; public: virtual void fun2() { cout<<"call A::fun2 success!"<<endl; } void fun() { cout<<this<<' '<<&m1<<' '<<hex<<*((int*)this)<<endl; //*((int*)this)取vptr的值 } }; int main() { A *pa1 = new A(); A *pa2 = new A(); pa1->fun(); pa2->fun(); return 1; }
輸出結果:
00332BA0 00332BA4 46F01C
00332BD8 00332BDC 46F01C
兩個對象的this值不同,數據成員的地址也不同,但是其中的vptr值是相同的。
5. 雖然子類重載了基類的虛函數,但是通過子類對象依然可以訪問到基類的虛函數。
看例子:
#include <iostream> using namespace std; class A { int m1; int m2; public: virtual void fun2() { cout<<"call A::fun2 success!"<<endl; } }; class B : public A { public: virtual void fun2() { cout<<"call B::fun2 success!"<<endl; } }; int main() { A *pa = new B(); pa->A::fun2(); return 1; }
輸出結果:
call A::fun2() success!
也就是說,在B的虛函數表中,依然存在着A的虛函數的入口地址,而且是可以調用的。