先看一個題目:
class Base
{
public:
virtual void Show(int x)
{
cout << "In Base class, int x = " << x << endl;
}
};
class Derived : public Base
{
public:
virtual void Show(float x)
{
cout << "In Derived, float x = " << x << endl;
}
};
void test (Base &b)
{
int i = 1;
b.Show(i);
float f = 2.0;
b.Show(f);
}
int main(int argc, char *argv[])
{
Base bc;
Derived sc;
test(bc);
test(sc);
return 0;
}
輸出結果為:D
A、In Base class, int x = 1;
In Base class, int x = 2;
In Derived, int x = 1;
In Derived, float x = 2;
B、In Base class, int x = 1;
In Base class, int x = 2;
In Derived, float x = 1;
In Derived, float x = 2;
C、In Base class, int x = 1;
In Base class, int x = 2;
In Base, int x = 1;
In Base, float x = 2;
D、In Base class, int x = 1;
In Base class, int x = 2;
In Base class, int x = 1;
In Base class, int x = 2;
理由:如果虛函數在基類與子類中出現的僅僅是名字的相同,而參數類型不同,或者傳回類型不同,即使寫上了virtual關鍵字,也不進行遲後聯編。
stackoverflow上,可以看到解釋,http://stackoverflow.com/questions/27227189/override-virtual-function-with-different-parameters-in-c
C++裡有兩種編譯類型:
1) 先期聯編或靜态聯編:在編譯時就能進行函數聯編稱為先期聯編或靜态聯編。
2) 遲後聯編或動态聯編:在運作時才能進行的聯編稱為遲後聯編或動态聯編。
virtual關鍵字的作用就是提示編譯器進行遲後聯編,告訴連結過程:“我是個虛的,先不要連接配接我,等運作時再說”。 具體原理:當編譯器遇到virtual後,會為所在的類構造一個表和一個指針,那個表叫做vtbl,每個類都有自己的vtbl,vtbl的作用就是儲存自己類中虛函數的位址,我們可以把vtbl形象地看成一個數組,這個數組的每個元素存放的就是虛函數的位址,指針叫做vptr,指向那個表。而這個指針儲存在相應的對象當中,也就是說隻有建立了對象以後才能找到相應虛函數的位址。
對于下面這種常見代碼(假如Base是Derive的父類):
Base *p=new Derive();
p->virtual_fun();
在程式運作時,根據對象的類型去初始化vptr,進而讓vptr正确的指向所屬類的虛表。上述程式中,由于p實際指向的對象類型是Derive,是以vptr指向的Derive類的vtable,當調用p->virtual_fun()時,根據虛表中的函數位址找到的就是Derive類的virtual_func()函數。
假設我們有這樣的一個類:
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
對應的虛函數表:

假設有如下所示的一個繼承關系:
對于執行個體:Derive d; 的虛函數表如下:
如果是多繼承:
而C++标準規定:為確定運作時的多态定義的基類與派生類的虛函數不僅函數名要相同,其傳回值及參數都必須相同,否則即使加上了virtual,系統也不進行遲後聯編。