base b;
fun pfun = null;
// invoke the first virtual function
pfun = (fun)*((int*)*(int*)(&b));
pfun();
實際執行經果如下:(windows xp+vs2003, linux 2.6.22 + gcc 4.1.3)
base::f
通過這個示例,我們可以看到,我們可以通過強行把&b轉成int *,取得虛函式表的位址,然後,再次取址就可以得到第乙個虛函式的位址了,也就是base::f(),這在上面的程式中得到了驗證(把int* 強制轉成了函式指標)。通過這個示例,我們就可以知道如果要呼叫base::g()和base::h(),其**如下:
(fun)*((int*)*(int*)(&b)+0); // base::f()
(fun)*((int*)*(int*)(&b)+1); // base::g()
(fun)*((int*)*(int*)(&b)+2); // base::h()
*注:事實上上面的**在我的機子上並不能列印出 base::g() 和 base::h()。發現這個函式的指標的大小是8bytes,所以給int指標加乙隻加了4個bytes,所以列印出來了segmentation fault,所以最好先得到fun的大小然後根據他來加,在我的機子上給加2就可以正確打出g和h了。
我們可以看到:
1) 每個父類都有自己的虛表。
2) 子類的成員函式被放到了第乙個父類的表中。(所謂的第乙個父類是按照宣告順序來判斷的)
看看我們可以用虛函式表來幹點什麼壞事吧。
一、通過父型別的指標訪問子類自己的虛函式
我們知道,子類沒有過載父類的虛函式是一件毫無意義的事情。因為多型也是要基於函式過載的。雖然在上面的圖中我們可以看到base1的虛表中有derive的虛函式,但我們根本不可能使用下面的語句來呼叫子類的自有虛函式:
base1 *b1 = new derive();
b1->f1(); //編譯出錯
任何妄圖使用父類指標想呼叫子類中的未覆蓋父類的成員函式的行為都會被編譯器視為非法,所以,這樣的程式根本無法編譯通過。但在執行時,我們可以通過指標的方式訪問虛函式表來達到違反c++語義的行為。(關於這方面的嘗試,通過閱讀後面附錄的**,相信你可以做到這一點)
二、訪問non-public的虛函式
另外,如果父類的虛函式是private或是protected的,但這些非public的虛函式同樣會存在於虛函式表中,所以,我們同樣可以使用訪問虛函式表的方式來訪問這些non-public的虛函式,這是很容易做到的。
如:class base {
private:
virtual void f() { cout << "base::f" <<>
class derive : public base{
typedef void(*fun)(void);
void main() {
derive d;
fun pfun = (fun)*((int*)*(int*)(&d)+0);
pfun();
C 虛繼承實現原理(虛基類表指標與虛基類表)
虛繼承和虛函式是完全無相關的兩個概念。虛繼承是解決c 多重繼承問題的一種手段,從不同途徑繼承來的同一基類,會在子類中存在多份拷貝。這將存在兩個問題 其一,浪費儲存空間 第二,存在二義性問題,通常可以將派生類物件的位址賦值給基類物件,實現的具體方式是,將基類指標指向繼承類 繼承類有基類的拷貝 中的基類...
C 虛指標 虛表
可分為靜態多型和動態多型 過載比較簡單這裡就不說,今天我們主要來談一談虛函式 如何驗證vptr指標存在?先看如下 父親類中沒有宣告虛函式,只有乙個int成員,而子類中宣告虛函式,並且擁有乙個int成員 include using namespace std class father class ch...
C 虛函式原理
簡單地說,每乙個含有虛函式 無論是其本身的,還是繼承而來的 的類都至少有乙個與之對應的虛函式表,其中存放著該類所有的虛函式對應的函式指標。例 其中 從編譯器的角度來說,b的虛函式表很好構造,d的虛函式表構造過程相對複雜。下面給出了構造d的虛函式表的一種方式 僅供參考 以下面的程式為例 編譯器只知道p...