程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++ COM編程之接口面前的虛函數表

C++ COM編程之接口面前的虛函數表

編輯:關於C++

C++ COM編程之接口面前的虛函數表。本站提示廣大學習愛好者:(C++ COM編程之接口面前的虛函數表)文章只能為提供參考,不一定能成為您想要的結果。以下是C++ COM編程之接口面前的虛函數表正文


媒介

進修C++的人,確定都曉得多態機制;多態就是用父類型其余指針指向其子類的實例,然後經由過程父類的指針挪用現實子類的成員函數。關於多態機制是若何完成的,你有無想過呢?而COM中的接口就將這一機制應用到了極致,所以,不曉得多態機制的人,是永運沒法明確COM的。所以,在總結COM時,長短常有需要專門總結一下C++的多態機制是若何完成的。

多態

甚麼是多態?下面也說了,多態就是用父類型其余指針指向其子類的實例,然後經由過程父類的指針挪用現實子類的成員函數。如今經由過程代碼,讓年夜家親身的領會一下多態:

#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();
}

下面代碼的運轉成果是:I am A.這不是多態的行動。

好了,經由對下面代碼的改革,就在A類的Print函數後面參加症結字virtual,詳細代碼以下:

#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();
}

此時,代碼的運轉成果為:I am B.這個時刻就表示出來了多態行動。好了,多了我也不說了,就經由過程這個簡略的例子,你就可以領會到多態的概念了。從上面才開端明天的主題。

虛函數表

多態機制的症結就是在於虛函數表,也就是vtbl。當我們界說一個類,類中包括虛函數時,其實也就界說了一張虛函數表,沒有虛函數的類是不包括虛函數表的,只要該類被實例化時,才會將這個表分派到這個實例的內存中;在這張虛函數表中,寄存了每一個虛函數的地址;它就像一個地圖一樣,指清楚明了現實所應當挪用的函數。好比我界說一個類,以下:


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;
};

如許的一個類,當你去界說這個類的實例時,編譯器會給這個類分派一個成員變量,該變量指向這個虛函數表,這個虛函數表中的每項都邑記載對應的虛函數的地址;以下圖:

這個類的變量還沒有被初始化時,就像上圖那樣,變量的值都是隨機值,而指向虛擬函數表的指針__vfptr中對應的虛函數地址也是毛病的地址;只要等我們真實的完成了這個變量的聲明和初始化時,這些值能力被准確的初始化,以下圖:

從上圖中便可以看到,初始化完成今後,指向虛函數表的__vfptr指針中的元素都被付與了准確的虛函數值,分離指向了在類中界說的三個虛函數。也看到了,__vfptr指針界說的地位也比m_iVar和m_fVar變量的地位靠前;在C++編譯器中,它包管虛函數表的指針存在於對象實例中最後面的地位,這重要是為了在多層繼續或是多重繼續的情形下,能以高機能取到這張虛函數表,然落後行遍歷,查找對應的虛函數指針,停止對應的挪用。

我們都曉得,虛函數是用來支撐C++中的多態的,而零丁的一個類,有了虛函數,而沒有任何繼續關系,也就是說沒有子類去籠罩父類的虛函數,如許是毫有意義的。所以上面就要從各個方面停止具體的解釋虛函數表。

沒有完成多態的單繼續

好比有以下的繼續關系:

在這個繼續關系中,CIF2作為CIF1的子類,然則CIF2沒有重寫CIF1類的任何虛函數;界說CIF2 if2Obj;實例,在派生類的實例中,它的虛函數表應當是像上面如許的:

[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 *

可以發明,虛函數依照其聲明次序寄存在表中,父類的虛函數在子類的虛函數後面。

完成多態的單繼續

如今我在CIF2類中,重寫CIF1類的IF1函數,它們的關系以下:

在上圖中,CIF2繼續了CIF1,而且在CIF2類中重寫了CIF1的虛函數IF1,那我們如今看看虛函數表是甚麼模樣的?


[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 *

你發明了甚麼?虛函數表中的第一項是CIF2::IF1,而不是CIF1::IF1,這解釋了當在子類中重寫父類的虛函數時,新的函數的地址籠罩了父類的虛函數地址,如許就可以在多態時能准確的找到須要被挪用的函數;而沒有被籠罩的函數照樣那樣的次序在虛函數表中存儲著。

沒有完成多態的多繼續

關於簡略的,沒有完成多態的多繼續,好比,有上面的一個多繼續關系:

在子類中沒有重寫任何父類的虛函數,那末它的虛函數表應當是甚麼模樣呢?

虛函數表CIF1,以下:

[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 *

虛函數表CIF2,以下:


[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 *

從下面的虛函數表,我們可以剖析出來,每一個父類都有本身的虛函數表,子類的虛函數被放到了第一個父類的表中。第一個父類是依照聲明次序來斷定的。

完成多態的多繼續

下面說的是沒有產生重寫的情形,如今來講說產生重寫的情形;好比,如今有以下情形:

在子類中重寫了父類的虛函數,那它的虛函數表又是甚麼模樣呢?

虛函數表CIF1,以下:


[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 *

虛函數表CIF2,以下:


[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 *

從下面的虛函數表中,我們可以看到虛函數表中的CIF1::IF1(void)全都被調換成了CIF3::IF1(void),那末我們便可以以隨意率性的父類指針來挪用IF1(void),現實上挪用的是CIF3::IF1(void),這就完成了所謂的多態。

總結

總結了這麼多關於虛函數表的內容,感到很扯,和接口沒有多年夜的關系;然則,這一切都是COM的基本,COM的面前,就是接口,而接口的面前,就是我這裡總結的,說白了,完整懂得了這裡,關於懂得COM的接口是有異常年夜的用途的。願望我的總結對年夜家有效。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved