久久99久久人婷婷精品综合_超碰aⅴ人人做人人爽欧美_亚洲电影第三页_日韩欧美一中文字暮专区_波多野结衣的一区二区三区_婷婷在线播放_人人视频精品_国产精品日韩精品欧美精品_亚洲免费黄色_欧美性猛交xxxxxxxx

C++虛函數在g++中的實現方法-創新互聯

這篇文章主要介紹C++虛函數在g++中的實現方法,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

我們提供的服務有:成都網站建設、成都網站制作、微信公眾號開發、網站優化、網站認證、鳩江ssl等。為1000多家企事業單位解決了網站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的鳩江網站制作公司

探索C++虛函數在g++中的實現

在開始之前,原諒我先借用一張圖黑一下C++:

“無敵”的C++

如果你也在寫C++,請一定小心…至少,你要先有所了解: 當你在寫虛函數的時候,g++在寫什么?

先寫個例子

為了探索C++虛函數的實現,我們首先編寫幾個用來測試的類,代碼如下:

C++

#include <iostream>

using namespace std;

class Base1
{
public:
    virtual void f() {
        cout << "Base1::f()" << endl;
    }
};

class Base2
{
public:
    virtual void g() {
        cout << "Base2::g()" << endl;
    }
};

class Derived : public Base1, public Base2
{
public:
    virtual void f() {
        cout << "Derived::f()" << endl;
    }

    virtual void g() {
        cout << "Derived::g()" << endl;
    }

    virtual void h() {
        cout << "Derived::h()" << endl;
    }
};

int main(int argc, char *argv[])
{
    Derived ins;
    Base1 &b1 = ins;
    Base2 &b2 = ins;
    Derived &d = ins;

    b1.f();
    b2.g();
    d.f();
    d.g();
    d.h();
}

代碼采用了多繼承,是為了更多的分析出g++的實現本質,用UML簡單的畫一下繼承關系:

C++虛函數在g++中的實現方法

示例代碼UML圖

代碼的輸出結果和預期的一致,C++實現了虛函數覆蓋功能,代碼輸出如下:

Derived::f()
Derived::g()
Derived::f()
Derived::g()
Derived::h()

開始分析!

我寫這篇文章的重點是嘗試解釋g++編譯在底層是如何實現虛函數覆蓋和動態綁定的,因此我假定你已經明白基本的虛函數概念以及虛函數表(vtbl)和虛函數表指針(vptr)的概念和在繼承實現中所承擔的作用,如果你還不清楚這些概念,建議你在繼續閱讀下面的分析前先補習一下相關知識,陳皓的 《C++虛函數表解析》 系列是一個不錯的選擇。

通過本文,我將嘗試解答下面這三個問題:

  1. g++如何實現虛函數的動態綁定?

  2. vtbl在何時被創建?vptr又是在何時被初始化?

  3. 在Linux中運行的C++程序虛擬存儲器中,vptr、vtbl存放在虛擬存儲的什么位置?

首先是第一個問題:

g++如何實現虛函數的動態綁定?

這個問題乍看簡單,大家都知道是通過vptr和vtbl實現的,那就讓我們刨根問底的看一看,g++是如何利用vptr和vtbl實現的。

第一步,使用 -fdump-class-hierarchy 參數導出g++生成的類內存結構:

Vtable for Base1
Base1::_ZTV5Base1: 3u entries
0     (int (*)(...))0
4     (int (*)(...))(& _ZTI5Base1)
8     Base1::f

Class Base1
   size=4 align=4
   base size=4 base align=4
Base1 (0xb6acb438) 0 nearly-empty
    vptr=((& Base1::_ZTV5Base1) + 8u)

Vtable for Base2
Base2::_ZTV5Base2: 3u entries
0     (int (*)(...))0
4     (int (*)(...))(& _ZTI5Base2)
8     Base2::g

Class Base2
   size=4 align=4
   base size=4 base align=4
Base2 (0xb6acb474) 0 nearly-empty
    vptr=((& Base2::_ZTV5Base2) + 8u)

Vtable for Derived
Derived::_ZTV7Derived: 8u entries
0     (int (*)(...))0
4     (int (*)(...))(& _ZTI7Derived)
8     Derived::f
12    Derived::g
16    Derived::h
20    (int (*)(...))-0x000000004
24    (int (*)(...))(& _ZTI7Derived)
28    Derived::_ZThn4_N7Derived1gEv

Class Derived
   size=8 align=4
   base size=8 base align=4
Derived (0xb6b12780) 0
    vptr=((& Derived::_ZTV7Derived) + 8u)
  Base1 (0xb6acb4b0) 0 nearly-empty
      primary-for Derived (0xb6b12780)
  Base2 (0xb6acb4ec) 4 nearly-empty
      vptr=((& Derived::_ZTV7Derived) + 28u)

如果看不明白這些亂七八糟的輸出,沒關系(當然能看懂更好),把上面的輸出轉換成圖的形式就清楚了:

C++虛函數在g++中的實現方法

vptr和vtbl

其中有幾點尤其值得注意:

  1. 我用來測試的機器是32位機,所有vptr占4個字節,每個vtbl中的函數指針也是4個字節

  2. 每個類的主要(primal)vptr放在類內存空間的起始位置(由于我沒有聲明任何成員變量,可能看不清楚)

  3. 在多繼承中,對應各個基類的vptr按繼承順序依次放置在類內存空間中,且子類與第一個基類共用同一個vptr

  4. 子類中聲明的虛函數除了覆蓋各個基類對應函數的指針外,還額外添加一份到第一個基類的vptr中(體現了共用的意義)

有了內存布局后,接下來觀察g++是如何在這樣的內存布局上進行動態綁定的。

g++對每個類的指針或引用對象,如果是其類聲明中虛函數,使用位于其內存空間首地址上的vptr尋找找到vtbl進而得到函數地址。如果是父類聲明而子類未覆蓋的虛函數,使用對應父類的vptr進行尋址。

先來驗證一下,使用 objdump -S 得到 b1.f() 的匯編指令:

Assembly (x86)

b1.f();
 8048734:       8b 44 24 24             mov    0x24(%esp),%eax    # 得到Base1對象的地址
 8048738:       8b 00                   mov    (%eax),%eax        # 對對象首地址上的vptr進行解引用,得到vtbl地址
 804873a:       8b 10                   mov    (%eax),%edx        # 解引用vtbl上第一個虛函數的地址
 804873c:       8b 44 24 24             mov    0x24(%esp),%eax
 8048740:       89 04 24                mov    %eax,(%esp)
 8048743:       ff d2                   call   *%edx              # 調用函數

其過程和我們的分析完全一致,聰明的你可能發現了,b2怎么辦呢?Derived類的實例內存首地址上的vptr并不是Base2類的??!答案實際上是因為g++在引用賦值語句 Base2 &b2 = ins 上動了手腳:

Assembly (x86)

Derived ins;
 804870d:       8d 44 24 1c             lea    0x1c(%esp),%eax
 8048711:       89 04 24                mov    %eax,(%esp)
 8048714:       e8 c3 01 00 00          call   80488dc <_ZN7DerivedC1Ev>
    Base1 &b1 = ins;
 8048719:       8d 44 24 1c             lea    0x1c(%esp),%eax
 804871d:       89 44 24 24             mov    %eax,0x24(%esp)
    Base2 &b2 = ins;
 8048721:       8d 44 24 1c             lea    0x1c(%esp),%eax   # 獲得ins實例地址
 8048725:       83 c0 04                add    $0x4,%eax         # 添加一個指針的偏移量
 8048728:       89 44 24 28             mov    %eax,0x28(%esp)   # 初始化引用
    Derived &d = ins;
 804872c:       8d 44 24 1c             lea    0x1c(%esp),%eax
 8048730:       89 44 24 2c             mov    %eax,0x2c(%esp)

雖然是指向同一個實例的引用,根據引用類型的不同,g++編譯器會為不同的引用賦予不同的地址。例如b2就獲得一個指針的偏移量,因此才保證了vptr的正確性。

PS:我們順便也證明了C++中的引用的真實身份就是指針…

接下來進入第二個問題:

vtbl在何時被創建?vptr又是在何時被初始化?

既然我們已經知道了g++是如何通過vptr和vtbl來實現虛函數魔法的,那么vptr和vtbl又是在什么時候被創建的呢?

vptr是一個相對容易思考的問題,因為vptr明確的屬于一個實例,所以vptr的賦值理應放在類的構造函數中。 g++為每個有虛函數的類在構造函數末尾中隱式的添加了為vptr賦值的操作 。

同樣通過生成的匯編代碼驗證:

Assembly (x86)

class Derived : public Base1, public Base2
{
 80488dc:       55                      push   %ebp
 80488dd:       89 e5                   mov    %esp,%ebp
 80488df:       83 ec 18                sub    $0x18,%esp
 80488e2:       8b 45 08                mov    0x8(%ebp),%eax
 80488e5:       89 04 24                mov    %eax,(%esp)
 80488e8:       e8 d3 ff ff ff          call   80488c0 <_ZN5Base1C1Ev>
 80488ed:       8b 45 08                mov    0x8(%ebp),%eax
 80488f0:       83 c0 04                add    $0x4,%eax
 80488f3:       89 04 24                mov    %eax,(%esp)
 80488f6:       e8 d3 ff ff ff          call   80488ce <_ZN5Base2C1Ev>
 80488fb:       8b 45 08                mov    0x8(%ebp),%eax
 80488fe:       c7 00 48 8a 04 08       movl   $0x8048a48,(%eax)
 8048904:       8b 45 08                mov    0x8(%ebp),%eax
 8048907:       c7 40 04 5c 8a 04 08    movl   $0x8048a5c,0x4(%eax)
 804890e:       c9                      leave
 804890f:       c3                      ret

可以看到在代碼中,Derived類的構造函數為實例的兩個vptr賦初值,可是,這兩個初值居然是立即數!立即數!立即數! 這說明了vtbl的生成并不是運行時的,而是在編譯期就已經確定了存放在這兩個地址上的 !

這個地址不出意料的屬于.rodata(只讀數據段),使用 objdump -s -j .rodata 提取出對應的內存觀察:

80489e0 03000000 01000200 00000000 42617365  ............Base
 80489f0 313a3a66 28290042 61736532 3a3a6728  1::f().Base2::g(
 8048a00 29004465 72697665 643a3a66 28290044  ).Derived::f().D
 8048a10 65726976 65643a3a 67282900 44657269  erived::g().Deri
 8048a20 7665643a 3a682829 00000000 00000000  ved::h()........
 8048a30 00000000 00000000 00000000 00000000  ................
 8048a40 00000000 a08a0408 34880408 68880408  ........4...h...
 8048a50 94880408 fcffffff a08a0408 60880408  ............`...
 8048a60 00000000 c88a0408 08880408 00000000  ................
 8048a70 00000000 d88a0408 dc870408 37446572  ............7Der
 8048a80 69766564 00000000 00000000 00000000  ived............
 8048a90 00000000 00000000 00000000 00000000  ................
 8048aa0 889f0408 7c8a0408 00000000 02000000  ....|...........
 8048ab0 d88a0408 02000000 c88a0408 02040000  ................
 8048ac0 35426173 65320000 a89e0408 c08a0408  5Base2..........
 8048ad0 35426173 65310000 a89e0408 d08a0408  5Base1..........

由于程序運行的機器是小端機,經過簡單的轉換就可以得到第一個vptr所指向的內存中的第一條數據為0x80488834,如果把這個數據解釋為函數地址到匯編文件中查找,會得到:

Assembly (x86)

08048834 <_ZN7Derived1fEv>:
};

class Derived : public Base1, public Base2
{
public:
    virtual void f() {
 8048834:       55                      push   %ebp
 8048835:       89 e5                   mov    %esp,%ebp
 8048837:       83 ec 18                sub    $0x18,%esp

Bingo! g++在編譯期就為每個類確定了vtbl的內容,并且在構造函數中添加相應代碼使vptr能夠指向已經填好的vtbl的地址 。

這也同時為我們解答了第三個問題:

在Linux中運行的C++程序虛擬存儲器中,vptr、vtbl存放在虛擬存儲的什么位置?

直接看圖:

C++虛函數在g++中的實現方法

虛函數在虛擬存儲器中的位置

圖中灰色部分應該是你已經熟悉的,彩色部分內容和相關聯的箭頭描述了虛函數調用的過程(圖中展示的是通過new在堆區創建實例的情況,與示例代碼有所區別,小失誤,不要在意): 當調用虛函數時,首先通過位于棧區的實例的指針找到位于堆區中的實例地址,然后通過實例內存開頭處的vptr找到位于.rodata段的vtbl,再根據偏移量找到想要調用的函數地址,最后跳轉到代碼段中的函數地址執行目標函數 。

總結

研究這些問題的起因是因為公司代碼出現了非常奇葩的行為,經過追查定位到虛函數表出了問題,因此才有機會腳踏實地的對虛函數實現進行一番探索。

也許你會想,即使我不明白這些底層原理,也一樣可以正常的使用虛函數,也一樣可以寫出很好的面相對象的代碼???

這一點兒也沒有錯,但是,C++作為全宇宙最復雜的程序設計語言,它提供的功能異常強大,無異于武俠小說中鋒利無比的屠龍寶刀。但武功不好的菜鳥如果胡亂舞弄寶刀,卻很容易反被其所傷。只有了解了C++底層的原理和機制,才能讓我們把C++這把屠龍寶刀使用的更加得心應手,變化出更加華麗的招式,成為真正的武林高手。

以上是C++虛函數在g++中的實現方法的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注創新互聯成都網站設計公司行業資訊頻道!

另外有需要云服務器可以了解下創新互聯scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業上云的綜合解決方案,具有“安全穩定、簡單易用、服務可用性高、性價比高”等特點與優勢,專為企業上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

新聞名稱:C++虛函數在g++中的實現方法-創新互聯
本文URL:http://www.js-pz168.com/article48/dichhp.html

成都網站建設公司_創新互聯,為您提供標簽優化網站維護網站導航、商城網站網頁設計公司品牌網站建設

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

成都定制網站網頁設計
久久99久久人婷婷精品综合_超碰aⅴ人人做人人爽欧美_亚洲电影第三页_日韩欧美一中文字暮专区_波多野结衣的一区二区三区_婷婷在线播放_人人视频精品_国产精品日韩精品欧美精品_亚洲免费黄色_欧美性猛交xxxxxxxx
欧美日韩国产中文| 制服丝袜亚洲播放| 欧美一级国产精品| 国产精品久久久久久久久晋中 | 日日骚一区二区网站| 欧美日韩国产另类一区| 中文字幕精品在线不卡| 日韩高清国产一区在线| 99久久国产综合精品麻豆 | 国产精品系列在线| 免费观看91视频大全| 91影院在线观看| 亚洲国产婷婷香蕉久久久久久99| 欧美一区二区三区在线观看| 国产精品的网站| 精品亚洲欧美一区| 国产精品美女xx| 在线观看视频一区二区欧美日韩| 久久久久综合网| 男女男精品网站| 超碰97在线人人| 欧美午夜视频网站| 亚洲欧美综合色| 国产美女在线观看一区| 欧美男人的天堂| 日韩视频免费观看高清完整版 | 色综合久久88色综合天天| 久久色在线观看| 日韩成人免费看| 国产精品嫩草在线观看| 欧美日韩你懂的| 亚洲黄一区二区三区| 成人av电影在线| 中文字幕一区二区三区5566| 欧美国产精品久久| 九色综合国产一区二区三区| 久久日韩精品| 精品国产91久久久久久久妲己| 日韩中文字幕一区二区三区| 高清视频在线观看一区| 欧美日韩国产精品成人| 亚洲一卡二卡三卡四卡| 99porn视频在线| 欧美精品日韩一区| 亚洲一区二区在线免费看| 91传媒视频在线观看| 欧美日韩国产影片| 亚洲成人免费视频| 国产精品美女诱惑| 日韩欧美一区二区不卡| 琪琪久久久久日韩精品| 久久伊人资源站| 久久婷婷成人综合色| 国产揄拍国内精品对白| 亚洲人成影视在线观看| 国产精品久久国产精麻豆99网站| 成人小视频免费观看| 欧美亚洲动漫另类| 亚洲一区在线观看免费 | 国产精品自拍在线| 伊人情人网综合| 亚洲乱码一区二区三区在线观看| 99在线视频精品| 欧美妇女性影城| 日本色综合中文字幕| 日本一区二区三区四区在线观看 | 蜜桃av噜噜一区二区三| 久久久久99精品国产片| 国产福利一区二区三区在线视频| 一本久道久久综合中文字幕| 一区二区激情小说| 国产乱码精品一区二区三区不卡| 精品久久人人做人人爱| 国产伦精品一区二区三区在线观看| 伊人av成人| 亚洲成人自拍偷拍| 欧美日韩一区二区三区在线视频| 国产精品嫩草久久久久| 91免费视频网址| 欧美va在线播放| 国产成人在线看| 欧美探花视频资源| 青青草国产成人99久久| 亚洲精品美女久久7777777| 亚洲精品视频在线| 久久精品丝袜高跟鞋| 欧美极品xxx| 91九色偷拍| 久久精品夜夜夜夜久久| av日韩在线网站| 精品乱人伦小说| 成人永久看片免费视频天堂| 91精品国产乱码| 国产精品一区二区在线播放 | 久久99精品一区二区三区| 色婷婷综合五月| 日日夜夜精品视频天天综合网| 视频二区一区| 亚瑟在线精品视频| 亚洲欧美日韩国产成人综合一二三区| 一区二区三区日韩精品视频| 乱色588欧美| 亚洲免费在线播放| 品久久久久久久久久96高清| 一区二区在线观看视频| 欧美在线播放一区二区| 一区二区三区精品视频| 台湾成人av| 无码av免费一区二区三区试看| 一区二区三区av在线| 日韩一区欧美二区| 91国模大尺度私拍在线视频| 麻豆成人久久精品二区三区红| 欧美亚洲一区二区在线| 狠狠色丁香婷综合久久| 这里只有精品99re| 成人少妇影院yyyy| 久久久久久久一区| 国产高清自拍一区| 亚洲日本一区二区| 色阁综合av| 人人狠狠综合久久亚洲| 欧美日韩精品三区| 丁香六月久久综合狠狠色| 精品福利一二区| 国产精品果冻传媒潘| 亚洲精品免费播放| 亚洲制服中文| 久久国产精品第一页| 欧美一区二区福利在线| 91免费视频大全| 最好看的中文字幕久久| 亚洲 国产 欧美一区| 麻豆91精品视频| 日韩一卡二卡三卡国产欧美| 91视频91自| 亚洲柠檬福利资源导航| 一级日韩一区在线观看| 国产做a爰片久久毛片| 日韩视频免费直播| 国产精品免费一区二区三区四区 | 亚洲在线不卡| 国产真实精品久久二三区| 精品国产一区二区三区忘忧草 | 成人晚上爱看视频| 国产精品网站一区| 视频一区二区在线观看| 精品一区免费av| 欧美精品一区二区三区蜜臀| 国产在线精品一区二区三区| 午夜电影网一区| 91精品在线免费观看| 99精品国产高清一区二区| 一区二区三区中文字幕在线观看| 色婷婷综合久久久久中文| 国产**成人网毛片九色 | 国产精品国模大尺度私拍| 亚洲一区二区三区在线| 欧美日韩免费高清一区色橹橹 | 久久日韩粉嫩一区二区三区| 欧美三日本三级少妇三99| 久久精品国产99久久6| 久久综合av免费| 欧美主播一区二区三区美女 久久精品人 | 欧美一区二区三区男人的天堂| av成人在线电影| 五月婷婷激情综合网| 日韩欧美一区中文| 久久久av水蜜桃| 黄色日韩三级电影| 欧美国产精品中文字幕| 色综合网色综合| www.久久精品| 亚洲福利一二三区| 日韩视频123| 日韩av大全| 国产v日产∨综合v精品视频| 日韩久久一区二区| 欧美日韩国产综合一区二区| 国产麻豆日韩| 精品一区二区三区在线观看| 国产欧美日韩精品a在线观看| 在线观看成人一级片| 2014亚洲精品| 日韩电影在线观看电影| 久久精品一区二区三区不卡| 在线精品日韩| av成人免费观看| 久久激五月天综合精品| 国产精品伦理在线| 欧美日韩美少妇| 久久涩涩网站| 国产精品99久久久久久宅男| 亚洲欧美激情视频在线观看一区二区三区 | 日韩精彩视频在线观看| 久久九九久精品国产免费直播| 亚洲欧洲精品在线| 91精品综合久久| 久久99国产乱子伦精品免费| 中文字幕在线观看一区二区|