最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ COM編程之接口背后的虛函數(shù)表

 更新時(shí)間:2014年10月01日 10:23:52   作者:果凍想  
這篇文章主要介紹了C++ COM編程之接口背后的虛函數(shù)表,COM的背后,就是接口,而接口的背后,就是虛函數(shù)表,需要的朋友可以參考下

前言

學(xué)習(xí)C++的人,肯定都知道多態(tài)機(jī)制;多態(tài)就是用父類(lèi)型別的指針指向其子類(lèi)的實(shí)例,然后通過(guò)父類(lèi)的指針調(diào)用實(shí)際子類(lèi)的成員函數(shù)。對(duì)于多態(tài)機(jī)制是如何實(shí)現(xiàn)的,你有沒(méi)有想過(guò)呢?而COM中的接口就將這一機(jī)制運(yùn)用到了極致,所以,不知道多態(tài)機(jī)制的人,是永運(yùn)無(wú)法明白COM的。所以,在總結(jié)COM時(shí),是非常有必要專(zhuān)門(mén)總結(jié)一下C++的多態(tài)機(jī)制是如何實(shí)現(xiàn)的。

多態(tài)

什么是多態(tài)?上面也說(shuō)了,多態(tài)就是用父類(lèi)型別的指針指向其子類(lèi)的實(shí)例,然后通過(guò)父類(lèi)的指針調(diào)用實(shí)際子類(lèi)的成員函數(shù)?,F(xiàn)在通過(guò)代碼,讓大家切身的體會(huì)一下多態(tài):

復(fù)制代碼 代碼如下:

#include <iostream>
using namespace std;
 
class A
{
public:
    void Print()
    {
        cout<<"I am A."<<endl;
    }
};
 
class B : public A
{
public:
    void Print()
    {
        cout<<"I am B."<<endl;
    }
};
 
int main()
{
    A *pAObj = new B();
    pAObj->Print();
}

上面代碼的運(yùn)行結(jié)果是:I am A.這不是多態(tài)的行為。

好了,經(jīng)過(guò)對(duì)上面代碼的改造,就在A類(lèi)的Print函數(shù)前面加入關(guān)鍵字virtual,具體代碼如下:

復(fù)制代碼 代碼如下:

#include <iostream>
using namespace std;
 
class A
{
public:
    virtual void Print()
    {
        cout<<"I am A."<<endl;
    }
};
 
class B : public A
{
public:
    void Print()
    {
        cout<<"I am B."<<endl;
    }
};
 
int main()
{
    A *pAObj = new B();
    pAObj->Print();
}

此時(shí),代碼的運(yùn)行結(jié)果為:I am B.這個(gè)時(shí)候就表現(xiàn)出來(lái)了多態(tài)行為。好了,多了我也不說(shuō)了,就通過(guò)這個(gè)簡(jiǎn)單的例子,你就能體會(huì)到多態(tài)的概念了。從下面才開(kāi)始今天的主題。

虛函數(shù)表

多態(tài)機(jī)制的關(guān)鍵就是在于虛函數(shù)表,也就是vtbl。當(dāng)我們定義一個(gè)類(lèi),類(lèi)中包含虛函數(shù)時(shí),其實(shí)也就定義了一張?zhí)摵瘮?shù)表,沒(méi)有虛函數(shù)的類(lèi)是不包含虛函數(shù)表的,只有該類(lèi)被實(shí)例化時(shí),才會(huì)將這個(gè)表分配到這個(gè)實(shí)例的內(nèi)存中;在這張?zhí)摵瘮?shù)表中,存放了每個(gè)虛函數(shù)的地址;它就像一個(gè)地圖一樣,指明了實(shí)際所應(yīng)該調(diào)用的函數(shù)。比如我定義一個(gè)類(lèi),如下:

復(fù)制代碼 代碼如下:

class CIF
{
public:
     CIF(){}
     CIF(int i, int f) : m_iVar(i), m_fVar(f){}
     virtual void IF1() { cout<<"I'm IF1"<<endl; }
     virtual void IF2() { cout<<"I'm IF2"<<endl; }
     virtual void IF3() { cout<<"I'm IF3"<<endl; }
     void MemFunc(){ cout<<"I'm IF4"<<endl; }
private:
     int m_iVar;
     float m_fVar;
};

這樣的一個(gè)類(lèi),當(dāng)你去定義這個(gè)類(lèi)的實(shí)例時(shí),編譯器會(huì)給這個(gè)類(lèi)分配一個(gè)成員變量,該變量指向這個(gè)虛函數(shù)表,這個(gè)虛函數(shù)表中的每一項(xiàng)都會(huì)記錄對(duì)應(yīng)的虛函數(shù)的地址;如下圖:

這個(gè)類(lèi)的變量還沒(méi)有被初始化時(shí),就像上圖那樣,變量的值都是隨機(jī)值,而指向虛擬函數(shù)表的指針__vfptr中對(duì)應(yīng)的虛函數(shù)地址也是錯(cuò)誤的地址;只有等我們真正的完成了這個(gè)變量的聲明和初始化時(shí),這些值才能被正確的初始化,如下圖:

從上圖中就可以看到,初始化完成以后,指向虛函數(shù)表的__vfptr指針中的元素都被賦予了正確的虛函數(shù)值,分別指向了在類(lèi)中定義的三個(gè)虛函數(shù)。也看到了,__vfptr指針定義的位置也比m_iVar和m_fVar變量的位置靠前;在C++編譯器中,它保證虛函數(shù)表的指針存在于對(duì)象實(shí)例中最前面的位置,這主要是為了在多層繼承或是多重繼承的情況下,能以高性能取到這張?zhí)摵瘮?shù)表,然后進(jìn)行遍歷,查找對(duì)應(yīng)的虛函數(shù)指針,進(jìn)行對(duì)應(yīng)的調(diào)用。

我們都知道,虛函數(shù)是用來(lái)支持C++中的多態(tài)的,而單獨(dú)的一個(gè)類(lèi),有了虛函數(shù),而沒(méi)有任何繼承關(guān)系,也就是說(shuō)沒(méi)有子類(lèi)去覆蓋父類(lèi)的虛函數(shù),這樣是毫無(wú)意義的。所以下面就要從各個(gè)方面進(jìn)行詳細(xì)的說(shuō)明虛函數(shù)表。

沒(méi)有實(shí)現(xiàn)多態(tài)的單繼承

比如有如下的繼承關(guān)系:

在這個(gè)繼承關(guān)系中,CIF2作為CIF1的子類(lèi),但是CIF2沒(méi)有重寫(xiě)CIF1類(lèi)的任何虛函數(shù);定義CIF2 if2Obj;實(shí)例,在派生類(lèi)的實(shí)例中,它的虛函數(shù)表應(yīng)該是像下面這樣的:

復(fù)制代碼 代碼如下:

[0]     0x011513c5 {InterfaceDemo2.exe!CIF1::IF1(void)}     void *
[1]     0x011512cb {InterfaceDemo2.exe!CIF1::IF2(void)}     void *
[2]     0x01151343 {InterfaceDemo2.exe!CIF1::IF3(void)}     void *
[3]     0x01151249 {InterfaceDemo2.exe!CIF2::IF4(void)}     void *
[4]     0x01151433 {InterfaceDemo2.exe!CIF2::IF5(void)}     void *
[5]     0x01151267 {InterfaceDemo2.exe!CIF2::IF6(void)}     void *
[6]     0x00000000     void *

可以發(fā)現(xiàn),虛函數(shù)按照其聲明順序存放在表中,父類(lèi)的虛函數(shù)在子類(lèi)的虛函數(shù)前面。

實(shí)現(xiàn)多態(tài)的單繼承

現(xiàn)在我在CIF2類(lèi)中,重寫(xiě)CIF1類(lèi)的IF1函數(shù),它們的關(guān)系如下:

在上圖中,CIF2繼承了CIF1,并且在CIF2類(lèi)中重寫(xiě)了CIF1的虛函數(shù)IF1,那我們現(xiàn)在看看虛函數(shù)表是什么樣子的?

復(fù)制代碼 代碼如下:

[0]     0x00b61311 {InterfaceDemo2.exe!CIF2::IF1(void)}     void *
[1]     0x00b612c6 {InterfaceDemo2.exe!CIF1::IF2(void)}     void *
[2]     0x00b61343 {InterfaceDemo2.exe!CIF1::IF3(void)}     void *
[3]     0x00b61249 {InterfaceDemo2.exe!CIF2::IF4(void)}     void *
[4]     0x00b61433 {InterfaceDemo2.exe!CIF2::IF5(void)}     void *
[5]     0x00000000     void *

你發(fā)現(xiàn)了什么?虛函數(shù)表中的第一項(xiàng)是CIF2::IF1,而不是CIF1::IF1,這說(shuō)明了當(dāng)在子類(lèi)中重寫(xiě)父類(lèi)的虛函數(shù)時(shí),新的函數(shù)的地址覆蓋了父類(lèi)的虛函數(shù)地址,這樣就能在多態(tài)時(shí)能正確的找到需要被調(diào)用的函數(shù);而沒(méi)有被覆蓋的函數(shù)還是那樣的順序在虛函數(shù)表中存儲(chǔ)著。

沒(méi)有實(shí)現(xiàn)多態(tài)的多繼承

對(duì)于簡(jiǎn)單的,沒(méi)有實(shí)現(xiàn)多態(tài)的多繼承,比如,有下面的一個(gè)多繼承關(guān)系:

在子類(lèi)中沒(méi)有重寫(xiě)任何父類(lèi)的虛函數(shù),那么它的虛函數(shù)表應(yīng)該是什么樣子呢?

虛函數(shù)表CIF1,如下:

復(fù)制代碼 代碼如下:

[0]     0x001e13d9 {InterfaceDemo2.exe!CIF1::IF1(void)}     void *
[1]     0x001e12df {InterfaceDemo2.exe!CIF1::IF2(void)}     void *
[2]     0x001e1357 {InterfaceDemo2.exe!CIF1::IF3(void)}     void *
[3]     0x001e10c8 {InterfaceDemo2.exe!CIF3::IF4(void)}     void *
[4]     0x001e1041 {InterfaceDemo2.exe!CIF3::IF5(void)}     void *
[5]     0x001e1249 {InterfaceDemo2.exe!CIF3::IF6(void)}     void *
[6]     0x00000000     void *

虛函數(shù)表CIF2,如下:

復(fù)制代碼 代碼如下:

[0]     0x001e1258 {InterfaceDemo2.exe!CIF2::IF7(void)}     void *
[1]     0x001e1447 {InterfaceDemo2.exe!CIF2::IF8(void)}     void *
[2]     0x001e127b {InterfaceDemo2.exe!CIF2::IF9(void)}     void *
[3]     0x00000000     void *

從上面的虛函數(shù)表,我們可以分析出來(lái),每個(gè)父類(lèi)都有自己的虛函數(shù)表,子類(lèi)的虛函數(shù)被放到了第一個(gè)父類(lèi)的表中。第一個(gè)父類(lèi)是按照聲明順序來(lái)判斷的。

實(shí)現(xiàn)多態(tài)的多繼承

上面說(shuō)的是沒(méi)有發(fā)生重寫(xiě)的情況,現(xiàn)在來(lái)說(shuō)說(shuō)發(fā)生重寫(xiě)的情況;比如,現(xiàn)在有以下情況:

在子類(lèi)中重寫(xiě)了父類(lèi)的虛函數(shù),那它的虛函數(shù)表又是什么樣子呢?

虛函數(shù)表CIF1,如下:

復(fù)制代碼 代碼如下:

[0]     0x012013cf {InterfaceDemo2.exe!CIF3::IF1(void)}     void *
[1]     0x012012d5 {InterfaceDemo2.exe!CIF1::IF2(void)}     void *
[2]     0x0120134d {InterfaceDemo2.exe!CIF1::IF3(void)}     void *
[3]     0x01201456 {InterfaceDemo2.exe!CIF3::IF4(void)}     void *
[4]     0x012014d8 {InterfaceDemo2.exe!CIF3::IF5(void)}     void *
[5]     0x00000000     void *

虛函數(shù)表CIF2,如下:

復(fù)制代碼 代碼如下:

[0]     0x012014e2 {InterfaceDemo2.exe![thunk]:CIF3::IF1`adjustor{4}' (void)}     void *
[1]     0x012014ce {InterfaceDemo2.exe!CIF2::IF2(void)}     void *
[2]     0x012014d3 {InterfaceDemo2.exe!CIF2::IF3(void)}     void *
[3]     0x00000000     void *

從上面的虛函數(shù)表中,我們可以看到虛函數(shù)表中的CIF1::IF1(void)全都被替換成了CIF3::IF1(void),那么我們就可以以任意的父類(lèi)指針來(lái)調(diào)用IF1(void),實(shí)際上調(diào)用的是CIF3::IF1(void),這就實(shí)現(xiàn)了所謂的多態(tài)。

總結(jié)

總結(jié)了這么多關(guān)于虛函數(shù)表的內(nèi)容,感覺(jué)很扯,和接口沒(méi)有多大的關(guān)系;但是,這一切都是COM的基礎(chǔ),COM的背后,就是接口,而接口的背后,就是我這里總結(jié)的,說(shuō)白了,完全了解了這里,對(duì)于理解COM的接口是有非常大的用處的。希望我的總結(jié)對(duì)大家有用。

相關(guān)文章

  • C語(yǔ)言堆棧入門(mén)指南

    C語(yǔ)言堆棧入門(mén)指南

    我身邊的一些編程的朋友以及在網(wǎng)上看帖遇到的朋友中有好多也說(shuō)不清堆棧,所以我想有必要給大家分享一下我對(duì)堆棧的看法,有說(shuō)的不對(duì)的地方請(qǐng)朋友們不吝賜教,這對(duì)于大家學(xué)習(xí)會(huì)有很大幫助
    2014-01-01
  • C++?反向迭代器模擬實(shí)現(xiàn)

    C++?反向迭代器模擬實(shí)現(xiàn)

    反向迭代器reverse_iterator是一種反向遍歷容器的迭代器,也就是從最后一個(gè)元素到第一個(gè)元素遍歷容器,本文主要介紹了C++?反向迭代器模擬實(shí)現(xiàn),感興趣的可以了解一下
    2024-01-01
  • C++簡(jiǎn)單實(shí)現(xiàn)Dijkstra算法

    C++簡(jiǎn)單實(shí)現(xiàn)Dijkstra算法

    這篇文章主要為大家詳細(xì)介紹了C++簡(jiǎn)單實(shí)現(xiàn)Dijkstra算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • 使用DeepSeek API 結(jié)合VSCode提升開(kāi)發(fā)效率

    使用DeepSeek API 結(jié)合VSCode提升開(kāi)發(fā)效率

    這篇文章主要介紹了DeepSeek API與Visual Studio Code (VSCode)結(jié)合使用,以提升軟件開(kāi)發(fā)效率,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-01-01
  • C++ 處理中文符號(hào)實(shí)例詳解

    C++ 處理中文符號(hào)實(shí)例詳解

    這篇文章主要介紹了C++ 處理中文符號(hào)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • C/C++ 運(yùn)用Npcap發(fā)送UDP數(shù)據(jù)包的完美過(guò)程

    C/C++ 運(yùn)用Npcap發(fā)送UDP數(shù)據(jù)包的完美過(guò)程

    UDP 是一種無(wú)連接、輕量級(jí)的傳輸層協(xié)議,與 TCP 相比,它不提供可靠性、流控制和錯(cuò)誤恢復(fù)機(jī)制,但卻更加簡(jiǎn)單且具有較低的開(kāi)銷(xiāo),這篇文章主要介紹了C/C++ 運(yùn)用Npcap發(fā)送UDP數(shù)據(jù)包,需要的朋友可以參考下
    2023-11-11
  • C/C++ 中extern關(guān)鍵字詳解

    C/C++ 中extern關(guān)鍵字詳解

    這篇文章主要介紹了C/C++ 中extern關(guān)鍵字詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 解析VC中預(yù)編譯頭文件的深入分析

    解析VC中預(yù)編譯頭文件的深入分析

    本篇文章是對(duì)VC中預(yù)編譯頭文件進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C語(yǔ)言樸素模式匹配算法實(shí)例代碼

    C語(yǔ)言樸素模式匹配算法實(shí)例代碼

    樸素模式匹配算法也稱(chēng)為布魯特-福斯算法,感覺(jué)很是高大上,但是實(shí)現(xiàn)起來(lái)很簡(jiǎn)單。這篇文章主要給大家介紹了關(guān)于C語(yǔ)言樸素模式匹配算法的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • 利用c++和easyx圖形庫(kù)做一個(gè)低配版掃雷游戲

    利用c++和easyx圖形庫(kù)做一個(gè)低配版掃雷游戲

    這篇文章主要介紹了用c++和easyx圖形庫(kù)做一個(gè)低配版掃雷游戲,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01

最新評(píng)論

全州县| 澜沧| 儋州市| 柯坪县| 平山县| 丰县| 赞皇县| 寻乌县| 和田市| 天全县| 丽江市| 日照市| 左贡县| 曲水县| 孟州市| 普兰店市| 依安县| 玛纳斯县| 吉水县| 承德市| 简阳市| 鹿邑县| 连州市| 隆德县| 宣化县| 南昌县| 河池市| 临邑县| 留坝县| 内丘县| 康定县| 汉阴县| 三江| 敖汉旗| 遂川县| 金沙县| 米脂县| 吉隆县| 吐鲁番市| 葫芦岛市| 漳浦县|