C++中,非虛函數都是靜態綁定,而虛函數卻是動態綁定。
爲了能夠更清楚地瞭解靜態綁定與動態綁定,我們可以看下面這個例子:
class B<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
{
public:
void fun();
...
}
class D: public B
{
...
}
D x;
B *pB = &x;
pB->fun();
D *pD = &x;
pD->fun();
那麼兩次調用fun()函數是否相同呢?當然,如果D中沒有定義fun()函數,那麼兩次調用的行爲肯定會是一樣的,但是調用過程卻不同。
pB->fun(); //pB爲指向B類型的指針,直接調用B中的fun()函數
pD->fun(); //pD爲指向D類型的指針,編譯器首先在D中查找fun()函數的聲明,沒有找到,然後到D的基類B中去找,找到fun(),停止查找。
但如果D中有自己定義的fun()函數,那執行結果就會不同:
pB->fun(); //調用B::fun()
pD->fun(); //調用D::fun()
原因就是,(non-virtual)非虛函數都是靜態綁定,也就是說,由於pB被聲明爲指向B類型的指針,那麼通過pB調用的(non-virtual)非虛函數永遠是B所定義的版本,即使pB指向一個類型爲“B的派生類D“的對象。
但另一方面,(virtual)虛函數卻是動態綁定,那麼不管是通過什麼類型的指針調用的這個虛函數,都會根據指針實際指向的對象類型來決定虛函數的調用,而與指針類型無關。如果fun()函數爲虛函數,那麼不管是通過pB還是pD調用fun()函數,都會調用D::fun(),因爲pB和pD真正指向的都是同一個類型D的對象。
因此在C++中,絕對不要重新定義繼承而來的(non-virtual)非虛函數,因爲這樣的話,函數的調用決定因素不在對象本身,而與調用函數的指針類型有關,這將給程序留下不可預知的隱患。
如上例,任何一個對象D都可能表現出B或D的行爲,決定因素不在x對象本身,而在於指向x的指針類型。
同時,絕對不要重新定義一個繼承而來的virtual函數的缺省參數值,因爲缺省參數值都是靜態綁定,而virtual函數卻是動態綁定。