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!

推薦閱讀: