欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++ 中的虛函數(shù)表及虛函數(shù)執(zhí)行原理詳解

 更新時(shí)間:2021年03月07日 10:03:44   作者:Er_HU  
這篇文章主要介紹了C++ 中的虛函數(shù)表及虛函數(shù)執(zhí)行原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

為了實(shí)現(xiàn)虛函數(shù),C++ 使用了虛函數(shù)表來(lái)達(dá)到延遲綁定的目的。虛函數(shù)表在動(dòng)態(tài)/延遲綁定行為中用于查詢調(diào)用的函數(shù)。

盡管要描述清楚虛函數(shù)表的機(jī)制會(huì)多費(fèi)點(diǎn)口舌,但其實(shí)其本身還是比較簡(jiǎn)單的。

首先,每個(gè)包含虛函數(shù)的類(或者繼承自的類包含了虛函數(shù))都有一個(gè)自己的虛函數(shù)表。這個(gè)表是一個(gè)在編譯時(shí)確定的靜態(tài)數(shù)組。虛函數(shù)表包含了指向每個(gè)虛函數(shù)的函數(shù)指針以供類對(duì)象調(diào)用。

其次,編譯器還在基類中定義了一個(gè)隱藏指針,我們稱為 *__vptr,*__vptr 是在類實(shí)例創(chuàng)建時(shí)自動(dòng)設(shè)置的,以指向類的虛函數(shù)表。*__vptr 是一個(gè)真正的指針,這和 *this 指針不同,*this 指針實(shí)際是一個(gè)函數(shù)參數(shù),使編譯器來(lái)達(dá)到自引用的目的。

結(jié)果就是,每個(gè)類對(duì)象都會(huì)多分配一個(gè)指針的大小,并且 *__vptr 是被派生類繼承的。

如果你不清楚這些組件是怎么配合運(yùn)作的,看下面的例子:

class Base
{
public:
  virtual void function1() {};
  virtual void function2() {};
};
 
class D1: public Base
{
public:
  virtual void function1() {};
};
 
class D2: public Base
{
public:
  virtual void function2() {};
};

因?yàn)檫@里有 3 個(gè)類,編譯器會(huì)創(chuàng)建 3 個(gè)虛函數(shù)表。

然后編譯器會(huì)在使用了虛函數(shù)的最上層基類中定義一個(gè)隱藏指針。盡管這個(gè)過(guò)程編譯器會(huì)自動(dòng)處理,但我們還是通過(guò)下面的例子來(lái)說(shuō)明指針添加的位置:

class Base
{
public:
  FunctionPointer *__vptr;
  virtual void function1() {};
  virtual void function2() {};
};
 
class D1: public Base
{
public:
  virtual void function1() {};
};
 
class D2: public Base
{
public:
  virtual void function2() {};
};

*__vptr 在類對(duì)象創(chuàng)建的時(shí)候會(huì)設(shè)置成指向類的虛函數(shù)表。例如,類型 Base 被實(shí)例化的時(shí)候,*__vptr 就指向 Base 的虛函數(shù)表。類型 D1 或者 D2 被實(shí)例化的時(shí)候,*__vptr 就指向 D1 或者 D2 的虛函數(shù)表。

現(xiàn)在我們來(lái)看下虛函數(shù)表是怎么創(chuàng)建的。因?yàn)槭纠忻總€(gè)類僅有 2 個(gè)虛函數(shù),所以每個(gè)虛函數(shù)表會(huì)存放兩個(gè)函數(shù)指針(分別指向 function1() 和 function2())。

Base 對(duì)象的虛函數(shù)表最簡(jiǎn)單。Base 對(duì)象只能訪問(wèn) Base 類型的成員,不能訪問(wèn) D1 或者 D2 的函數(shù)。所以 Base 的虛函數(shù)表中的兩個(gè)指針?lè)謩e指向 Base::function1() 和 Base::function2()。

D1 的虛函數(shù)表稍復(fù)雜點(diǎn),D1 對(duì)象能夠訪問(wèn) D1 以及 Base 的成員。D1 重寫(xiě)了 function1(),但沒(méi)有重寫(xiě) function2(),所以 D1 的虛函數(shù)表中的兩個(gè)指針?lè)謩e指向 D1::function1() 和 Base::function2()。

D2 的虛函數(shù)表同理 D1,包含了分別指向 Base::function1() 和 D2::function2() 的指針。

考慮如果創(chuàng)建 D1 對(duì)象時(shí)會(huì)發(fā)生什么:

int main()
{
  D1 d1;
}

因?yàn)?d1 是 D1 類型對(duì)象,d1 有它自己的 *__vptr 指向 D1 類型的虛函數(shù)表。

現(xiàn)在創(chuàng)建一個(gè) Base 類型指針 *dPtr 指向 d1:

int main()
{
  D1 d1;
  Base *dPtr = &d1;
 
  return 0;
}

重點(diǎn):

因?yàn)?dPtr 是 Base 類型指針,它只指向 d1 對(duì)象的 Base 類型部分(即,指向 d1 對(duì)象中的 Base 子對(duì)象),而 *__vptr 也在 Base 類型部分。所以 dPtr 可以訪問(wèn) Base 類型部分中的 *__vptr。同時(shí),這里注意,dPtr->__vptr 指向的是 D1 的虛擬函數(shù)表,這是在 d1 初始化時(shí)就確定的。所以結(jié)果,盡管 dPtr 是 Base 類型指針,但它能夠訪問(wèn) D1 的虛函數(shù)表。

因此,當(dāng)有調(diào)用 dPtr->function1() 時(shí),發(fā)生了什么?

int main()
{
  D1 d1;
  Base *dPtr = &d1;
  dPtr->function1();
 
  return 0;
}

首先,程序識(shí)別到 function1() 是一個(gè)虛函數(shù)。

其次,程序使用 dPtr->__vptr 獲取到了 D1 的虛函數(shù)表。

然后,它在 D1 的虛函數(shù)表中尋找可以調(diào)用的 function1() 版本,這里是 D1::function1()。

因此,dPtr->function1() 實(shí)際調(diào)用了 D1::function1()。

通過(guò)虛函數(shù)表,編譯器和程序能夠確定調(diào)用什么版本的虛函數(shù),盡管使用的是指向/引用基類的指針或者引用。

調(diào)用虛函數(shù)會(huì)比調(diào)用非虛函數(shù)更慢,有以下幾個(gè)原因:

  • 必須使用 *__vptr 獲取正確的虛函數(shù)。
  • 必須建立虛函數(shù)表的索引來(lái)獲取想要調(diào)用的函數(shù)。
  • 調(diào)用找到的函數(shù)。

結(jié)果就是必須進(jìn)行三次操作才能完成對(duì)函數(shù)的調(diào)用。但是對(duì)于現(xiàn)代計(jì)算機(jī)系統(tǒng),這些額外操作增加的時(shí)間幾乎可以忽略不計(jì)。

另外,每個(gè)使用虛函數(shù)表的類都有 *__vptr 指針,從而每個(gè)類對(duì)象都會(huì)多一個(gè)指針的空間。虛函數(shù)很強(qiáng)大,但是它確實(shí)產(chǎn)生了性能開(kāi)銷。

到此這篇關(guān)于C++ 中的虛函數(shù)表及虛函數(shù)執(zhí)行原理詳解的文章就介紹到這了,更多相關(guān)C++ 虛函數(shù)表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談C語(yǔ)言中的注釋風(fēng)格小結(jié)

    淺談C語(yǔ)言中的注釋風(fēng)格小結(jié)

    今天小編就為大家分享一篇淺談C語(yǔ)言中的注釋風(fēng)格小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-12-12
  • QT5連接MySQL實(shí)現(xiàn)增刪改查

    QT5連接MySQL實(shí)現(xiàn)增刪改查

    這篇文章主要為大家詳細(xì)介紹了QT5如何連接MySQL實(shí)現(xiàn)增刪改查功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的可以了解一下
    2022-12-12
  • C語(yǔ)言實(shí)現(xiàn)單詞小助手功能完善版

    C語(yǔ)言實(shí)現(xiàn)單詞小助手功能完善版

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)單詞小助手功能的完善版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • C語(yǔ)言實(shí)現(xiàn)任意進(jìn)制轉(zhuǎn)換器

    C語(yǔ)言實(shí)現(xiàn)任意進(jìn)制轉(zhuǎn)換器

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)任意進(jìn)制轉(zhuǎn)換器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 淺談#ifndef,#define,#endif的作用和用法

    淺談#ifndef,#define,#endif的作用和用法

    下面小編就為大家?guī)?lái)一篇淺談#ifndef,#define,#endif的作用和用法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-12-12
  • 使用VS2010創(chuàng)建MFC ActiveX工程項(xiàng)目

    使用VS2010創(chuàng)建MFC ActiveX工程項(xiàng)目

    VS2010開(kāi)發(fā)ActiveX有兩種方法,分別是MFC和ATL。MFC開(kāi)過(guò)起來(lái)比較簡(jiǎn)單,但是最終生成的文件比較大,ATL是專門用來(lái)開(kāi)發(fā)ActiveX的,但是相對(duì)比較難,必須知道很多原理機(jī)制和API。咱先從MFC開(kāi)發(fā)ActiveX開(kāi)始吧。
    2015-06-06
  • C語(yǔ)言三個(gè)函數(shù)的模擬實(shí)現(xiàn)詳解

    C語(yǔ)言三個(gè)函數(shù)的模擬實(shí)現(xiàn)詳解

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言三個(gè)函數(shù)的模擬實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • 詳解C++編程中的sizeof運(yùn)算符與typeid運(yùn)算符

    詳解C++編程中的sizeof運(yùn)算符與typeid運(yùn)算符

    這篇文章主要介紹了C++編程中的sizeof運(yùn)算符與typeid運(yùn)算符,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2016-01-01
  • EasyC++內(nèi)部鏈接性和無(wú)鏈接性

    EasyC++內(nèi)部鏈接性和無(wú)鏈接性

    這篇文章主要介紹了EasyC++內(nèi)部鏈接性和無(wú)鏈接性,當(dāng)我們使用static關(guān)鍵字,將變量的作用于限制在整個(gè)文件時(shí),該變量的鏈接性為內(nèi)部鏈接性,然而無(wú)鏈接性的變量其實(shí)就是在代碼塊當(dāng)中使用static關(guān)鍵字創(chuàng)建的,接下來(lái)一起進(jìn)入文章了解更多內(nèi)容吧
    2021-12-12
  • C語(yǔ)言數(shù)組元素的循環(huán)移位方法

    C語(yǔ)言數(shù)組元素的循環(huán)移位方法

    今天小編就為大家分享一篇C語(yǔ)言數(shù)組元素的循環(huán)移位方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07

最新評(píng)論