虛函式的工作原理
獲取物件的虛函式表位址
給類函式宣告virtual
關鍵字,將該函式解釋為虛函式。基類定義的虛函式表明子類可以繼承並實現該虛函式。虛函式的最大作用為:可以通過基類指標或引用來接收乙個子類指標或者引用,並通過基類指標或引用來呼叫虛函式來實現相同的動作不同的實現方式,達到多型的目的。
例子**如下:
#include
#include
using
namespace std;
class
baseclass;}
;class
deriveclass
:public baseclass };
intmain()
上述**如果將baseclass::showme()
定義為普通函式,那麼雖然使用的baseclass *bc接收的deriveclass的指標,得到的結果依然是baseclass::showme()
。
如果將baseclass::showme()
定義為虛函式,那麼baseclass *bc接收的deriveclass的指標,將會根據物件來判斷,最終會呼叫deriveclass::showme()
。
這裡涉及到靜態聯編和動態聯編。
如果乙個類的函式未宣告virtual
,那麼在呼叫該函式處,編譯器根據呼叫函式的型別進行靜態聯編,也就是在編譯期間就已經定好呼叫哪個位址的函式。
如果乙個類的函式通過virtual
宣告為了虛函式,那麼該函式呼叫處,無法在編譯期確定呼叫基類的函式還是派生類的函式,所以在呼叫函式處加入動態函式呼叫的**。在程式執行時根據物件的虛函式表來查詢最終呼叫的函式,並呼叫它。
虛析構函式
如果乙個類將被作為基類,那麼該類應該將析構函式宣告為虛函式。因為只有這樣,才能確保派生類指標賦值給基類指標時,基類和派生類的釋放順序是正確的。
#include
#include
using
namespace std;
class
baseclass};
class
deriveclass
:public baseclass };
intmain()
如果baseclass::~baseclass()
未定義為虛函式,那麼delete bc的動作不會呼叫deriveclass::~deriveclass()
,只會呼叫父類的baseclass::~baseclass()
。
只有基類的析構函式定義為虛函式:virtual ~baseclass()
那麼在多型發生時,才會先呼叫派生類的析構函式,然後呼叫基類的析構函式。
對於虛函式的處理,編譯器會為每個物件開始處新增乙個隱藏成員,來儲存乙個指向函式位址陣列的指標。該函式位址陣列叫虛函式表(virtualfunctiontable,vtbl),虛函式表裡面存放的每個元素都是派生類與基類的每個虛函式的位址。
基類物件包含乙個指標,該指標指向存放了基類宣告的所有虛函式的虛函式表。派生類物件也包含乙個指標指向了派生類的虛函式表,但是派生類的虛函式表從基類繼承複製過來的,如果派生類重新定義了基類的虛函式,那麼派生類的虛函式表中對應的虛函式位址就是派生類的虛函式位址;如果沒有重新定義基類的虛函式,那麼派生類的虛函式表存放的還是指向基類的虛函式位址。
#include
#include
using
namespace std;
class
baseclass
;virtual
void
two();
};class
deriveclass
:public baseclass
virtual
void
three()
};intmain()
如上**deriveclass繼承了baseclass並重新定義了one虛函式,以及新增了three的虛函式。
基類和派生類對應的虛函式表分別如下:
從上圖看出,deriveclass的虛函式表one()和three()指向的是自己的函式位址,two()指向的是父類的函式位址。所以執行上面**得到如下結果:
子類shome(
)父類two(
)子類three(
)
#include
using
namespace std;
class
animal
public
:virtual
void
print_age
(void)=
0;virtual
void
print_kind()
=0;virtual
void
print_status()
=0;}
;class
dog:
public animal
~dog()
virtual
void
print_age
(void
)virtual
void
print_kind()
virtual
void
print_status()
};class
cat:
public animal
~cat()
virtual
void
print_age
(void
)virtual
void
print_kind()
virtual
void
print_status()
};void
print_random_message
(void
* something)
intmain
(void);
*((intptr_t*
*) pa)
= fake_vtable;
pa->
print_age()
;// woof, my age = 1
pa->
print_kind()
;// i'm a cat
pa->
print_status()
;// i'm crazy
return0;
}
其他參考資料: c 虛函式與虛函式表原理
目錄 用virtual修飾的成員函式叫虛函式 小知識 沒有虛建構函式 不寫虛函式,沒有預設的虛函式 普通函式不影響類的記憶體 class mm protected void testvirtual int main 輸出 1 增加乙個指標的記憶體,32位作業系統多4個位元組 64位作業系統多8個位元...
虛函式的工作原理
通常,編譯器處理虛函式的方法是 給每個物件新增乙個隱藏成員。隱藏成員中儲存 乙個指向函式位址陣列的指標。這種陣列稱為虛函式表 virtual function table,vtbl 虛函式表中儲存了為類物件進行宣告的虛函式的位址。總之,使用虛函式時,在記憶體和執行速度方面有一定的成本。包括 1 每個...
C 虛函式原理
簡單地說,每乙個含有虛函式 無論是其本身的,還是繼承而來的 的類都至少有乙個與之對應的虛函式表,其中存放著該類所有的虛函式對應的函式指標。例 其中 從編譯器的角度來說,b的虛函式表很好構造,d的虛函式表構造過程相對複雜。下面給出了構造d的虛函式表的一種方式 僅供參考 以下面的程式為例 編譯器只知道p...