C++中的虛函數的作用主要是實現了多態的機制。關于多態,簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調用實際子類的成員函數。這種技術可以讓父類的指針有“多種形態”,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的代碼來實現可變的算法。比如:模板技術,RTTI技術,虛函數技術,要么是試圖做到在編譯時決議,要么試圖做到運行時決議。
1 虛函數表
虛函數(Virtual Function)是通過一張虛函數表(Virtual Table)來實現的。簡稱為V-Table。 在這個表中,主是要一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題,保證其容真實反應實際的函數。這樣,在有虛函數的類的實例中這個表被分配在了 這個實例的內存中,所以,當我們用父類的指針來操作一個子類的時候,這張虛函數表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。
這里我們著重看一下這張虛函數表。在C++的標準規格說明書中說到,編譯器必需要保證虛函數表的指針存在于對象實例中最前面的位置(這是為了保證正確取到虛函數的偏移量)。 這意味著我們通過對象實例的地址得到這張虛函數表,然后就可以遍歷其中函數指針,并調用相應的函數。
2 代碼:
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
typedef void (*Fun) (void);
class Base{
public:
virtual void f() {cout<<"Base::f"<<endl;}
virtual void g() {cout<<"Base::g"<<endl;}
virtual void h() {cout<<"Base::h"<<endl;}
};
void main(int argc,char * argv[])
{
Base b;
Fun pFun = NULL;
int *var1= (int *)&b;
cout<<"虛函數表地址:"<<var1<<endl; //(int*)(&b)
int *var2 = (int*)*(int*)(&b);
cout<<"虛函數表-第一個函數地址:"<< var2 << endl;//(int*)*(int*)(&b)
//Invoke the first virtual function
pFun =
(Fun)*((int*)*(int*)(&b)+0); //f()
pFun();
pFun = (Fun)*((int*)*(int*)(&b)+1); //g()
pFun();
pFun = (Fun)*((int*)*(int*)(&b)+2); // h()
pFun();
}
//結果
虛函數表地址:0x0012FF7C
虛函數表-第一個函數地址:0x0042804C
Base::f
Base::g
Base::h