国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

深入解析C++中的虛函數與多態

2020-01-26 15:49:49
字體:
來源:轉載
供稿:網友

1.C++中的虛函數
C++中的虛函數的作用主要是實現了多態的機制。關于多態,簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調用實際子類的成員函數。這種技術可以讓父類的指針有“多種形態”,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的代碼來實現可變的算法。比如:模板技術,RTTI技術,虛函數技術,要么是試圖做到在編譯時決議,要么試圖做到運行時決議。

對C++ 了解的人都應該知道虛函數(Virtual Function)是通過一張虛函數表(Virtual Table)和一個指向虛函數表的指針(vptr)來實現的。虛函數表,簡稱為vtbl,虛函數表表對實現多態起著至關重要的作用。在這個表中,主要保存了一個類中的虛函數的地址,這張表解決了繼承、覆蓋的問題,保證其內容能真實反應實際的函數。每一個包含虛函數的類的實例都包含一個cptr指針,指向虛函數表的首地址。我們可以通過這個指針找到要訪問的虛函數的,完成虛函數的調用主要包括:找到虛函數表的首地址(vptr),通過cptr找到要使用虛函數地址,調用虛函數。那么使用虛函數大家總要考慮效率的問題,實際上為了提高效率,C++的編譯器是保證虛函數表的指針存在于對象實例中最前面的位置,這是為了保證取到虛函數表的有最高的性能,這意味著我們通過對象實例的地址得到這張虛函數表,然后通過遍歷表就可以找到其中的虛函數的地址,然后調用相應的函數。不妨看看下面的代碼:

復制代碼 代碼如下:

#include <iostream>

using namespace std;

class Base
{
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
};

typedef void(*Fun)(void);


int main()
{
    Base b;
    Fun pFun = NULL;
    cout << "虛函數表地址:" << (int*)(&b) << endl;
    cout << "虛函數表 ― 第一個函數地址:" << (int*)*(int*)(&b) << endl;
    pFun = (Fun)*((int*)*(int*)(&b));
    pFun();
    return 0;
}


通過這個示例,可以看到,通過強行把&b轉成int *,取得虛函數表的地址(vptr),然后,再次取址就可以得到第一個虛函數的地址了,也就是Base::f(),這在上面的程序中得到了驗證(把int* 強制轉成了函數指針)。通過這個示例,我們就可以知道如果要調用Base::g()和Base::h(),其代碼如下:
復制代碼 代碼如下:

(Fun)*((int*)*(int*)(&b)+0); // Base::f()

(Fun)*((int*)*(int*)(&b)+1); // Base::g()

(Fun)*((int*)*(int*)(&b)+2); // Base::h()


可以看看虛函數表的圖是怎么畫的:

            

大家都知道,多態是通過繼承實現的,那么我們要說說虛函數繼承的問題。繼承就涉及到了虛函數的覆蓋了,實際上不被覆蓋的虛函數和多態又有什么聯系呢?這里我們討論有覆蓋的虛函數表是什么樣的,假設存在下面的繼承關系:

        

看看虛函數表示什么樣的:

        

可以發現,Base::f()被覆蓋了,這樣若把Derive的實例賦值給一個基類Base指針pBase,通過pBase->f();則訪問的是子類中的f()也就是完成了多態。那么虛函數表中的內容到底是怎么樣的呢?可以看看下面的四句話就會明白!

1.虛函數按照其聲明順序放于表中。

2.父類的虛函數在子類的虛函數前面。

3.覆蓋的f()函數被放到了虛表中原來父類虛函數的位置。

4.沒有被覆蓋的函數依舊。

2.用虛函數實現多態
看看下面的多態的代碼:

復制代碼 代碼如下:

#include <iostream>

using namespace std;

class Base
{
public:
    virtual void Print()
    {
        cout<<"Base::Print()"<<endl;
    }
};
class Derive : public Base
{
public:
    virtual void Print()
    {
        cout<<"Derive::Print()"<<endl;
    }
};
int main()
{
    Derive derive;
    Base *pBase = &derive;
    pBase->Print();
    return 0;
}
//多態代碼


實現虛函數的代碼,一定要切記:一定是基類的指針指向子類的對象的地址。首先試著理解一下用虛函數實現多態的原理,如果實在沒理解為什么虛函數能實現多態,又為什么這樣實現多態,上網再搜一搜相關的資料!!!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 屏山县| 博爱县| 遵义市| 石嘴山市| 辽源市| 福泉市| 齐河县| 乌鲁木齐市| 文水县| 始兴县| 阜平县| 荆门市| 潼关县| 西丰县| 蓬溪县| 英吉沙县| 镇江市| 上虞市| 安吉县| 西乌珠穆沁旗| 丽水市| 社会| 额敏县| 宁城县| 平南县| 舒城县| 电白县| 龙井市| 偃师市| 天长市| 灵山县| 江口县| 凌源市| 乌拉特前旗| 霸州市| 桦川县| 清原| 秭归县| 富锦市| 日土县| 平度市|