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

淺談C++ 虛函數(shù)

 更新時(shí)間:2020年09月14日 15:31:46   作者:bianchengnan  
這篇文章主要介紹了C++ 虛函數(shù)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下

緣起

在上一篇文章中,測(cè)試代碼2 中的 pBaseA->AA(); 輸出的內(nèi)容很“奇怪”。其實(shí),完全在情理之中。本文將簡(jiǎn)單探究一下 c++ 中的虛函數(shù)實(shí)現(xiàn)機(jī)制。本文主要基于 vs2013 生成的 32 位代碼進(jìn)行研究,相信其它編譯器(比如,gcc)的實(shí)現(xiàn)大同小異。

先從對(duì)象大小開(kāi)始

假設(shè)我們有如下代碼,假設(shè) int 占 4 字節(jié),指針占 4 字節(jié)。

#include "stdafx.h"
#include "stdlib.h"
#include "stddef.h"

class CBase
{
public:
  virtual void VFun1() { printf(__FUNCTION__ "\n"); }
  virtual void VFun2() { printf(__FUNCTION__ "\n"); }
  virtual ~CBase() { printf(__FUNCTION__ "\n"); }
  int data;
};

class CDerived : public CBase
{
public:
  virtual void VFunNew() { printf(__FUNCTION__ "\n"); }
  virtual void VFun1() override { printf(__FUNCTION__ "\n"); }
  virtual ~CDerived() override { printf(__FUNCTION__ "\n"); }
};

int _tmain(int argc, _TCHAR* argv[])
{
  printf("sizeof CBase is: %d, offset of data is %d\n",
      sizeof(CBase), offsetof(CBase, data));
  system("pause");

  CBase* pBase = new CDerived();
  pBase->VFun1();
  pBase->VFun2();
  system("pause");
  return 0;
}

輸出結(jié)果如下圖:

有沒(méi)有覺(jué)得意外?從類定義可知,data 占 4 字節(jié),那另外的 4 字節(jié)是哪里來(lái)的呢?data 的偏移值不應(yīng)該是 0 嗎?為什么是 4 呢?

內(nèi)存布局

如果一個(gè)類有虛函數(shù),編譯器會(huì)自動(dòng)為這個(gè)類型的對(duì)象在頭部增加一個(gè)虛表指針(vftable),指向虛函數(shù)表。虛函數(shù)表中存放著一個(gè)個(gè)的虛函數(shù)。

CBaseCDerived 類對(duì)象的內(nèi)存布局如下:

注意:虛函數(shù)表中索引為 -1 的地方指向了跟動(dòng)態(tài)類型轉(zhuǎn)換相關(guān)的信息。

虛表指針的初始化

vftable 是在類的構(gòu)造函數(shù)中初始化的??梢栽?code> IDA 中分別查看 CBase 類 和 CDerived 類的構(gòu)造函數(shù)的反匯編代碼。

CBase 構(gòu)造函數(shù)的反匯編代碼如下(關(guān)鍵部分已注釋):

由反匯編代碼可知, CBase 的構(gòu)造函數(shù)會(huì)把 CBase 對(duì)象開(kāi)始的位置(存放虛表指針)設(shè)置為 CBase::vftable。

CDerived 構(gòu)造函數(shù)的反匯編代碼如下(關(guān)鍵部分已注釋):

由反匯編代碼可知, CDerived 的構(gòu)造函數(shù)會(huì)先調(diào)用 CBase 的構(gòu)造函數(shù)進(jìn)行基類部分的初始化,在 CBase 構(gòu)造函數(shù)的內(nèi)部把 CDerived 對(duì)象開(kāi)始的位置設(shè)置為 CBase::vftable,然后調(diào)用自身的初始化部分,會(huì)把 CDerived::vftable 的地址放到對(duì)象開(kāi)始的位置,從而替換掉了 CBase 類的虛表指針。

虛函數(shù)表的內(nèi)容

了解完了虛表指針的初始化過(guò)程,再來(lái)看看 vftable 里面都有哪些內(nèi)容。

可以雙擊 ??_7CBase@@6B@ (或者直接按回車)跳轉(zhuǎn)到虛表所在的地方。如下圖:

說(shuō)明:上側(cè)是 CBase 類的虛表內(nèi)容,下側(cè)是 CDerived 類的虛表內(nèi)容。

請(qǐng)注意圖片上側(cè)黃色高亮部分,也就是 vftable[-1] 的地方,是跟動(dòng)態(tài)類型轉(zhuǎn)換相關(guān)的信息,后面有機(jī)會(huì)介紹。

虛函數(shù)調(diào)用

理解了類對(duì)象的內(nèi)存布局及虛函數(shù)表之后,再理解虛函數(shù)的調(diào)用過(guò)程就比較簡(jiǎn)單了。

有些 C++ 基礎(chǔ)的小伙伴兒都知道本例中的輸出結(jié)果應(yīng)該如下圖所示:

直接看一下 pBase->VFun1() pBase->VFun2() 對(duì)應(yīng)的反匯編代碼就應(yīng)該明白一切了。如下圖:

因?yàn)?pBase 指向的實(shí)際是 CDerived 類型的對(duì)象,所以虛表是 CDerived 類的。如下圖所示

經(jīng)過(guò)以上的分析,輸出結(jié)果合情合理。

說(shuō)明

本文只是拿了一個(gè)最最簡(jiǎn)單的例子做演示。像多重繼承,虛繼承等比較復(fù)雜的情況,感興趣的小伙伴可以自行研究。

雖然這個(gè)例子很簡(jiǎn)單,但是背后的機(jī)理值得了解清楚,非常有用。比如,當(dāng)庫(kù)中的接口與庫(kù)頭文件不匹配的時(shí)候,很可能莫名其妙的就崩潰了。這時(shí)可以通過(guò)查看指針對(duì)應(yīng)的虛表的內(nèi)容來(lái)查看庫(kù)中的虛函數(shù)都有哪些,跟頭文件對(duì)比后就可以比較準(zhǔn)確的判斷是否是庫(kù)不匹配的問(wèn)題。還可以根據(jù)虛表的內(nèi)容,猜測(cè)出基類指針指向的具體的子類對(duì)象的類型。

可以在 windbg 中使用 dps 命令快速打印,如下圖:

總結(jié)

  • 虛表指針是在類的構(gòu)造函數(shù)中初始化的,相應(yīng)的代碼由編譯器自動(dòng)生成。
  • 在生成調(diào)用虛函數(shù)的代碼的時(shí)候,并沒(méi)有直接把虛函數(shù)地址寫死,而是通過(guò)虛表進(jìn)行調(diào)用,多了一層間接層。
  • Any problem in computer science can be solved by anther layer of indirection. (計(jì)算機(jī)科學(xué)領(lǐng)域的任何問(wèn)題都可以通過(guò)增加一個(gè)間接的中間層來(lái)解決)

注意:如果通過(guò)對(duì)象調(diào)用虛函數(shù),會(huì)是另外一種情況,因?yàn)椴淮嬖诙鄳B(tài),直接使用函數(shù)低級(jí)進(jìn)行調(diào)用就可以了。感興趣的小伙伴兒可以自行實(shí)驗(yàn)。

參考資料

深度探索 c++ 對(duì)象模型

以上就是淺談C++ 虛函數(shù)的詳細(xì)內(nèi)容,更多關(guān)于C++ 虛函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解C++ 模板編程

    詳解C++ 模板編程

    模板(template)是C++實(shí)現(xiàn)泛型(Generics)和元編程(Meta Programming)的基礎(chǔ)。本文拋磚引玉,簡(jiǎn)要介紹C++模板編程,不足之處敬請(qǐng)指正。
    2020-09-09
  • OpenCV實(shí)現(xiàn)圖像去噪算法的步驟詳解

    OpenCV實(shí)現(xiàn)圖像去噪算法的步驟詳解

    這篇文章主要為大家介紹了OpenCV中圖像去噪算法的原理,文中通過(guò)示例為大家詳細(xì)講解了圖像去噪算法的使用,感興趣的小伙伴可以了解一下
    2022-06-06
  • 手把手教你如何一眼分辨是C還是C++

    手把手教你如何一眼分辨是C還是C++

    在很大程度上,C++是C的超集,這意味著一個(gè)有效的C程序也是一個(gè)有效的C++程序,下面這篇文章主要給大家介紹了關(guān)于如何一眼分辨是C還是C++的相關(guān)資料,需要的朋友可以參考下
    2023-02-02
  • 深入探討C++父類子類中虛函數(shù)的應(yīng)用

    深入探討C++父類子類中虛函數(shù)的應(yīng)用

    本篇文章是對(duì)C++父類子類中虛函數(shù)的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++?如何將Lambda轉(zhuǎn)換成函數(shù)指針

    C++?如何將Lambda轉(zhuǎn)換成函數(shù)指針

    這篇文章主要介紹了C++?如何將Lambda轉(zhuǎn)換成函數(shù)指針,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • C++實(shí)現(xiàn)超市商品管理系統(tǒng)最新版

    C++實(shí)現(xiàn)超市商品管理系統(tǒng)最新版

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)超市商品管理系統(tǒng)最新版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • 帶你了解C++的數(shù)組與函數(shù)

    帶你了解C++的數(shù)組與函數(shù)

    這篇文章主要介紹了C++的數(shù)組與函數(shù),包括數(shù)組的初始化等基本知識(shí),需要的朋友可以參考下,希望能給你帶來(lái)幫助
    2021-08-08
  • C語(yǔ)言中.與->的用法介紹

    C語(yǔ)言中.與->的用法介紹

    結(jié)構(gòu)體用點(diǎn),結(jié)構(gòu)體指針用箭頭,簡(jiǎn)單的說(shuō),就是一個(gè)快捷方式,一個(gè)語(yǔ)法糖,這篇文章主要介紹了C語(yǔ)言中.與->的區(qū)別及用法,需要的朋友可以參考下
    2023-05-05
  • C++核心編程之占位參數(shù)和默認(rèn)參數(shù)

    C++核心編程之占位參數(shù)和默認(rèn)參數(shù)

    這篇文章主要介紹了C++核心編程之占位參數(shù)和默認(rèn)參數(shù),c++中函數(shù)的形參列表中的形參是可以有默認(rèn)值的,函數(shù)的形參列表里可以有占位參數(shù),用來(lái)占位,調(diào)用函數(shù)時(shí)必須填補(bǔ)位置。下面更多相關(guān)內(nèi)容的詳細(xì)介紹,需要的小伙伴可以參考一下
    2022-03-03
  • C語(yǔ)言目標(biāo)文件的詳細(xì)講解

    C語(yǔ)言目標(biāo)文件的詳細(xì)講解

    最近正在閱讀關(guān)于C語(yǔ)言的庫(kù),但是我還沒(méi)有find關(guān)于目標(biāo)文件的解釋,這篇文章主要給大家介紹了C語(yǔ)言目標(biāo)文件的詳細(xì)講解,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01

最新評(píng)論

岫岩| 黄梅县| 津市市| 丘北县| 桐庐县| 织金县| 南雄市| 莱阳市| 建平县| 武清区| 昌都县| 水城县| 青冈县| 巴楚县| 莱阳市| 台东市| 永年县| 米泉市| 武宁县| 若羌县| 南靖县| 麻栗坡县| 天门市| 建平县| 盐亭县| 兴安盟| 石渠县| 轮台县| 淮安市| 凌云县| 和田县| 苏州市| 滨州市| 湄潭县| 尼勒克县| 阳山县| 永登县| 容城县| 松江区| 嵊泗县| 东山县|