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

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

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

緣起

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

先從對象大小開始

假設(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é)果如下圖:

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

內(nèi)存布局

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

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

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

虛表指針的初始化

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

說明

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

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

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

總結(jié)

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

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

參考資料

深度探索 c++ 對象模型

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

相關(guān)文章

  • C++制作簡單的計算器功能

    C++制作簡單的計算器功能

    這篇文章主要為大家詳細(xì)介紹了C++制作簡單的計算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C++ 中virtual 虛函數(shù)用法深入了解

    C++ 中virtual 虛函數(shù)用法深入了解

    這篇文章主要介紹了C++ 中virtual 虛函數(shù)用法深入了解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • C語言進(jìn)階輸入輸出重定向與fopen函數(shù)使用示例詳解

    C語言進(jìn)階輸入輸出重定向與fopen函數(shù)使用示例詳解

    這篇文章主要為大家介紹了C語言進(jìn)階輸入輸出重定向與fopen函數(shù)的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • C++11中std::declval的實(shí)現(xiàn)機(jī)制淺析

    C++11中std::declval的實(shí)現(xiàn)機(jī)制淺析

    這篇文章主要給大家介紹了關(guān)于C++11中std::declval實(shí)現(xiàn)機(jī)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-07-07
  • Qt圖形圖像開發(fā)之高性能曲線圖模塊QCustomplot庫詳細(xì)使用方法與實(shí)例(支持動、靜曲線圖)

    Qt圖形圖像開發(fā)之高性能曲線圖模塊QCustomplot庫詳細(xì)使用方法與實(shí)例(支持動、靜曲線圖)

    這篇文章主要介紹了Qt圖形圖像開發(fā)之高性能曲線圖模塊QCustomplot庫詳細(xì)使用方法與實(shí)例(支持動、靜曲線圖),需要的朋友可以參考下
    2020-03-03
  • C++編程語言中賦值運(yùn)算符重載函數(shù)(operator=)的使用

    C++編程語言中賦值運(yùn)算符重載函數(shù)(operator=)的使用

    本文主要介紹了C++編程語言中賦值運(yùn)算符重載函數(shù)(operator=)介紹,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Java?C++?leetcode面試零矩陣

    Java?C++?leetcode面試零矩陣

    這篇文章主要為大家介紹了Java?C++題解leetcode面試零矩陣示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • C語言中的操作符優(yōu)先級的詳細(xì)介紹

    C語言中的操作符優(yōu)先級的詳細(xì)介紹

    這篇文章主要介紹了C語言中的操作符優(yōu)先級的詳細(xì)介紹的相關(guān)資料,希望通過本文能幫助到大家,大家通過本文能徹底掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-08-08
  • C/C++?單元自動化測試解決方案總結(jié)

    C/C++?單元自動化測試解決方案總結(jié)

    這篇文章主要介紹了C/C++?單元自動化測試解決方案總結(jié),通過利用GCC插件來實(shí)現(xiàn)提升C/C++開發(fā)者的單元效率工具解決方案,希望對大家在提升單元測試效率上有所啟發(fā)
    2022-06-06
  • C語言通過gets和gets_s分別實(shí)現(xiàn)讀取含空格的字符串

    C語言通過gets和gets_s分別實(shí)現(xiàn)讀取含空格的字符串

    在遇到包含空格的字符串輸入時該如何讀取呢?如果使用scanf以%s格式去讀取輸入的字符串,遇到空格就讀取結(jié)束了,顯然這樣是讀取不了的。本文就將介紹兩個可以對含空格字符串讀取的庫函數(shù)------gets和gets_s函數(shù),感興趣的可以了解一下
    2021-12-12

最新評論