基本介紹
- 中文名:虛調用
- 外文名:virtual call
- 學科領域:計算機編程
- 程式語言:C++
1.虛函式的幾種實調用的情形
1.1不通過指針或者引用調用虛函式
虛調用不能簡單的理解成“對虛函式的調用”,因為對虛函式的調用很有可能是是調用。考察如下程式。
#include <iostream>using namespace std; class A{public: virtual void show(){ cout<<"in A"<<endl; }}; class B:public A{public: void show(){ cout<<"in B"<<endl; }}; void func(A a){ a.show();} int main(){ B b; func(b);}
程式運行輸出結果是:in A。在函式func()中,雖然在class A中函式show()被定義為虛函式,但是由於a是類A的一個示例,而不是指向類A對象的指針或者引用,所以函式調用a.show()是實調用,函式的入口地址是在編譯階段靜態決定的。
函式調用func(b)的執行過程是這樣的:先由對象b通過類A的賦值構造函式,產生一個類A的對象作為函式func()的實參進入函式體。在函式體內,a是一個“純粹”的類A的對象,與類型B毫無關係,所以a.show()是實調用。
1.2構造函式和析構函式中調用虛函式
在構造函式和析構函式中調用虛函式,對虛函式的調用實際航是實調用。這是虛函式被“實調用”的另一個例子。由於從概念上說,在一個對象的構造函式運行完畢之前,這個對象還沒有完全誕生,所以在構造函式中調用虛函式,實際上都是實調用。
析構時,在銷毀一個對象時,先調用該類所屬類的析構函式,然後再調用其基類的析構函式。所以,在調用基類的析構函式時,派生類已經被析構了,派生類數據成員已經失效,無法動態的調用派生類的虛函式。
考察如下例子。
#include <iostream>using namespace std; class A{public: virtual void show(){ cout<<"in A"<<endl; } A(){show();} ~A(){show();}}; class B:public A{public: void show(){ cout<<"in B"<<endl; }}; int main(){ A a; B* pb=new B(); cout<<"after new"<<endl; delete pb; cout<<"after delete"<<endl;}
程式的執行結果是:
in A
in A
after new
in A
after delete
in A
在構造類B的對象b時,會先調用基類A的構造函式,如果在構造函式中對show()的調用是虛調用,那么應該列印出“in B”。析構也是如此,對虛函式的調用是實調用。因此,一般情況下,應該避免在構造函式和析構函式中調用虛函式,如果一定要這樣做,程式猿必須清楚,這是對虛函式的調用其實是實調用。
2.虛調用的常見形式
設立虛函式的初衷就是想在設計基類的時候,對該基類的派生類實施一定程度的控制。籠統的說,就是“通過基類訪問派生類成員”。
因此,虛調用最常見的形式是:通過指向基類的指針或引用來訪問派生類對象的虛函式。這種情況較為常見。
不常見形式:
不過由於虛調用是通過查詢虛函式表來實現的,而擁有虛函式的對象都可以訪問道所屬類的虛函式表,所以,一個不常見的做法是通過指向派生類對象的指針或引用調用基類對象的虛函式,考察如下代碼。
#include <iostream>using namespace std; class A{public: virtual void show(){ cout<<"in A"<<endl; }}; class B:public A{public: void show(){ cout<<"in B"<<endl; }}; int main(){ A a; B& br=static_cast<B&>(a); br.show();}
程式輸出結果是:in A。通過派生類對象的引用rB實現了對基類中虛函式show()的調用,如果在class A的定義中,將函式show()前面的關鍵字virtual去掉,那么程式的執行結果就是in B,br.show()就變成了實調用。
3.虛調用一定要藉助於指針或引用來實現嗎
答案是否定的。在實際套用中,絕大多數的虛調用的確是顯示藉助於指針或者引用來實現,但是可以通過間接的方式來實現虛調用。
#include <iostream>using namespace std; class A{public: virtual void show(){ cout<<"in A"<<endl; } void callfunc(){show();}}; class B:public A{public: void show(){ cout<<"in B"<<endl; }}; int main(){ B b; b.callfunc();}
程式的執行結果是:in B。在這個程式中,看不到一個指針或者引用,卻發生了虛調用。函式調用b.callfunc()執行的實際上是A::func(),如果在class A中卻掉函式show()前面的關鍵字virtual,那么程式的輸出結果是:in A。也就是說,在函式callfunc()中,函式調用show()是一個虛調用,它是在運行時才決定使用派生類中的虛函式還是實用基類中的虛函式。