C++淺析虛函數使用方法
闡述虛函數作用和原理、純虛函數和虛函數的區別。
一、虛函數
首先來看下面這一段代碼,首先創建兩個類,一個是Dog,另一個是Cat,他們有一個共同的屬性:Run。在定義中每個動物都需要創建一個類,比較繁瑣,所以在下面的例子中,我們可以把他們簡化。
#include <iostream> using namespace std; class Dog{ public: void Run(){ cout<<"Dog->Run"<<endl; } }; class Cat{ public: void Run(){ cout<<"Cat->Run"<<endl; } }; int main() { Dog d; d.Run(); Cat c; c.Run(); return 0; }
這裡使用多態和虛函數,而Animal提供統一的接口,供子類使用,雖然代碼繁瑣,但提高瞭整個工程的可擴展性和靈活性。
在普通函數前加上關鍵字 virtual 構成虛函數,子類需要重寫父類的虛函數,這樣在調用的時候,會覆蓋掉父類的虛函數 Run,去執行子類的Run。
#include <iostream> using namespace std; class Animal{ public: virtual void Run(){ cout<<"Animal->Run"<<endl; } }; class Dog :public Animal{ public: void Run(){ cout<<"Dog->Run"<<endl; } }; class Cat:public Animal{ public: void Run(){ cout<<"Cat->Run"<<endl; } }; int main() { Animal *ani; ani = new Dog; ani->Run(); delete ani; ani = new Cat; ani->Run(); delete ani; return 0; }
結果如下:
所以在這裡隻需要修改ani的指向就可以實現不同方法。如果不存在虛函數,把Animal類的關鍵詞virtual去掉會怎麼樣呢,顯然,他們會默認實現父類Run的方法。
class Animal{ public: void Run(){ cout<<"Animal->Run"<<endl; } };
所以引入虛函數是為瞭實現動態多態,指向不同的子類來實現不同的方法。
二、虛函數與純虛函數的區別
因為父類的函數可以不做任何操作,所以這裡可以直接等於0;實現純虛函數。
//虛函數 class Animal{ public: virtual void Run(){ cout<<"Animal->Run"<<endl; } };
//純虛函數 class Animal{ public: virtual void Run()=0; };
虛函數與純虛函數的區別:
純虛函數隻是一個接口,隻能供子類去重寫實現方法。而虛函數在裡面也可以去實現父類的功能。隻需要指向父類的方法即可。
總結:虛函數在子類裡面也可以不進行重寫,但純虛函數必須在子類去實現,如果把子類中的Run方法去掉,隻留下父類中的純虛函數,那麼編譯器會報錯,這裡大傢可以試試。
三、動態多態
Animal內部的結構是什麼樣呢?這裡有一個虛函數指針(vfptr)和虛函數表(vftable)。 指針(vfptr)指向虛函數表,在虛函數表(vftable)內記錄著虛函數的地址,即Run函數的地址。
當子類的Dog去繼承父類後,父類的虛函數表相應的也繼承下來,子類也會保存一份和父類相同的。
註意!這時候如果發生重寫,即子類重寫瞭父類的虛函數,則子類的虛函數表會覆蓋父類繼承下來的虛函數表。但父類的虛函數表不會發生改變。
當父類的指針或者引用指向子類的對象時,就發生瞭多態。
下面的代碼中是指向瞭Dog,所以會去Dog的虛函數表中找到相應的函數,在運行階段發生瞭動態多態。
Animal *ani; ani = new Dog; ani->Run();
到此這篇關於C++淺析虛函數使用方法的文章就介紹到這瞭,更多相關C++虛函數內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!