對於含有虛函數的類,編譯器會自動爲其生成一個一維的虛函數表vtable,用來存儲虛函數的入口地址。然後在實例化類對象的時候,會自動創建一個指向該類虛函數表的指針vptr。
當一個子類繼承父類(含虛函數)時,會賦值一個相同的虛函數表。然後如果子類中對父類中的虛函數進行重寫(注意:兩個函數的函數名、函數標籤都相同時纔算同一個函數),那麼就會再開闢一片空間來存儲重寫過的函數,子類的vtable中相應的入口地址也會被修改成這個重寫函數的入口地址。
class A
{
public:
int a,b;
void fun1(){...};
void fun2(){...};
virtual void virfun1(){...};// 假設存儲該函數的地址爲1
virtual void virfun2(){...};//假設存儲該函數的地址爲2
};
class B public:A
{
public:
int c;
virtual void virfun1(){xxxxxx};//假設存儲該函數的地址爲3.
};
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 在某一類的具體實例中,內存中只保存了該實例的狀態變量(成員變量)以及一個指向該類虛函數表的指針vptr,而沒有成員函數。對於非虛函數,是在編譯時編譯器自動爲成員函數綁定入口地址的,而虛函數則要在運行時動態綁定(查水錶!。。好吧,是虛函數表。。)</span>
上面一段代碼中,A類中,假設virfun1對應的地址爲1,virfun2對應的虛函數表爲2, 則 虛函數應該是 1,2。 然後B類, 由於重寫了virfun1(假設地址爲3),其虛函數表應該是3,2。
另外,在虛函數通常是方便指針變量的使用的, 比如:
A* p = new B;
這個在下一篇文章 ,虛函數&繼承 中會說到。。;