日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

图解C++虚函数 虚函数表

發布時間:2025/4/5 c/c++ 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图解C++虚函数 虚函数表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

圖解C++虛函數

2016年07月02日 17:47:17?海楓?閱讀數:5181?標簽:?虛函數c++g++對象模型C++虛函數更多

個人分類:?C/C++/linux

版權聲明:本文為博主原創文章,承蒙轉載請注明作者和出處 https://blog.csdn.net/linyt/article/details/51811314

介紹

早在5年前寫過《從匯編層面深度剖析C++虛函數》一文,介紹C++的虛函數表和調用過程。最近在看OSv操作系統代碼,迫不得已看了C++11中的新語法,最后還是跳不出虛函數的五指山。本文盡量使用圖來解釋虛函數在類,繼承,多繼承各種場下的對象模型結構,以及虛函數實現多態綁定。

值得注意的是,不同編譯器生成的對象結構和虛函數表稍為有一些不同,本文均采用gcc 5.3.0版本下的g++編譯器作為研究對象。

普通類

object類的定義

class object {int a;int b;public:object(): a(0), b(1) {}virtual void f() {} };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

上述代碼中定義object類,定義兩個int成員,分別是a和b,然后定義了虛函數f。

object對象內存結構

下圖是定義兩個object對象o1和o2的內存結構。

所有帶虛函數類對象的首4字節為虛函數表(vtable)指針,object也是如此。object對象的第一個4字節為vtable指針,它指向object全局的虛函數表,每個類只需一個vtable表即可。o1和o2共享一個虛函數表。?
虛函數表的內容依次是:object::f(),object::g()

接下來8個字節是意想不到的東西,那就是object類的type_info對象,這由編譯器生成的對象結構,它與C++庫中的type_info內存部局完全相同。?
g++編譯器將類的type_info對象信息放到了vtable表的尾部。

調用虛函數過程

下面圖片描述了object指針調用虛函數的過程。?

具體過程可解釋如下:

  • 從o對象中找到它的虛函數表地址
  • 根據g函數在虛函數表中的offset,該函數地址
  • 根據函數地址進行調用
  • 獲取type_info對象

    C++的RTTI機制本該屬于別一個話題,不適合在虛函數中談論。但在具體實現過程中,編譯器將它和vtable合并到一起,所以還在有必要簡單討論RTTI機制。

    由于type_info信息也是放到vtable里面,那可以認為typeid操作符是虛函數一部分,它在vtable也有一個offset.

    下面是object對象獲取它的type_info引用的過程。

    與其它虛函數調用類似,typeid返回的type_info對象就是vtable尾部的type_info對象。?
    每個類只有一個type_inof對象,不能被修改,所以typeid操作符只能是返回const引用。

    可以想象一下typeid(o).name()就是返回type_info對象是name成員指向的字符器串”6object”。

    繼承類

    父類和子類定義

    下面代碼定義父類base和子類derive.

    class base {int b; public:virtual void f() {}virtual void g() {} };class derive: public base {int d; public:virtual void g() {} };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    base對象和derive對象內存結構

    derive子類重寫了g()函數,所以它的vtable中的第二項為derive::g(),而f()函數沒有重寫,所以第一項仍然是base::f()函數。

    多態的實現

    我們經常看到這樣的代碼:

    base *b = new derive(); b->g();
    • 1
    • 2

    在b->g()調用過程中,調用的是derive::g()函數,而不是base::g(),是如何實現的呢?這其中的奧秘就是虛函數表中。詳見下圖。

    b對象盡管是base*類型的,但它的地址跟new出來derive對象地址是同一個(后面多重繼承例子中就不是這樣子的了),所以在調用b->g()時,從vtable指向的虛函數中找第二項,它值為derive::g()函數的地址,所以最終調用的是derive::g()函數。

    多重繼承

    多重繼承是更復雜的一個場景,在多重繼承的情況下,子類指針向基類指針轉換時,它的地址是不一樣的,所以編譯必須生成一些額外代碼來做地址轉換。

    多重繼承類定義

    class base1 {int b1; public:virtual void f1() {}virtual void g1() {} };class base2 {int b2; public:virtual void f2() {}virtual void g2() {} };class base3 {int b3; public:virtual void f3() {}virtual void g3() {} };class derive: public base1, public base2, pbulic base3 {int d; public:virtual void f1() {}virtual void f2() {}virtual void f3() {} };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    基類的對象內存結構

    下圖分別定義base1, base2, base3基類對象,不需過多解釋。

    派生類的對象內存結構

    從這個圖開始,我們開始要燒腦了。下圖是derive對象d的內存結構:

    derive對象內存結構有以下幾個特點:

  • base1, base2, base3這3個基類依次排列,后面才是derive類新增的d成員
  • derive對象有3個虛函數表指針(請注意不是1個了,這里面大有戲法)?
    3.derive對象有前8字節,也是base1基類所在坑的位置;它的vtable指針指向的虛函數表,供derive類型使用,也供base1類型使用。對于base1類型,只使用前兩項。而derive類型,則使用更多項
  • derive類的虛函數表中:前兩項的排列是與base1完全一樣的,而后面的derive::f2(),derive::f3(),則是dervice類重載base2/base3虛函數的總列表。
  • derive另外兩個虛函數表,以base2坑的虛函數表為例,它有兩項。第一項是non-virtual thunk to derive::f2(),第二項是base2::g2()。因為derive類沒有重寫g2函數,所以第二項填base2::g2()是乎合理解的。而non-virtul thunk to derive::f2()這項我們后面會解釋。
  • 其它的-8和-16數字,估計是其它語法場景下有用,目前沒有看到,可以先跳過它們。
  • 另外在整個derive的虛函數表中,出現兩次derive類的type_info指針,先忽略它們吧。
  • 派生類向基類轉換的秘密

    也許你知道,派生類對象轉基類對象轉換之后,這兩者的地址都是一樣的,而在多重繼承里面,這個結論就不對了。

    從上面看到,派生類是將基類依次排列而成。所以派生類對象指針向第一個基類指針轉換時,兩者地址是一樣的;而第二個和第三個基類對象指針轉換時,它的地址就不一樣的。請看下圖:

    派生類調用非重寫函數

    以這兩行代碼為例?
    derive *d = new derive();?
    d->g2();

    顯然,derive沒有重寫g2()函數,所以它調用的是base2類的虛函數。?
    其實,不管derive是否有重寫g2函數,都是通過base2的虛函數表找出來的。具體過程如下圖所示:

    由于g2函數是最早是由base2類定義的,所以d->g2()調用時,先從d對象中的base2虛函數表,查找g2偏移量(值為4)的表項,再調用。

    但這里有個細節一定要注意的是,base2::g2函數的this指針是base2 *類型的,而這里的d是derive*類型的,需要先將derive?*指針轉換成base2*指針。這個轉換完成之后,指針值就增加8字節了。

    多重繼承下的多態實現

    這里詳細分析

    base2 *b2 = new derive(); b2->f2();
    • 1
    • 2

    是如何實現從基類到派生類f2()函數的調用。

    b2指針已指向了derive對象的base2部分,然后b2->f2()從base2-vtable對應的虛函數表的第一項,找到了non-virtual thunk to derive::f2(),然后調用。

    咦,這里不應該是derive::f2()嗎,那個non-virtual thunk to derive::f2()是什么鬼?

    答案是和this指針強相關

    derive::f2()函數的this指針肯定是derive*類型的,而這里的b2是base2*類型,不能直接調用。

    non-virtual thunk to derive::f2()代碼其實是兩行匯編,它完成出b2指針從base*類型轉換成derive*類型的功能,也即地址減去8。

    小結

    其實我想只用圖表將C++虛函數全部表達出來,但當我畫出來之后,發現很多細節不用文字稍作說明,不是很難明白。

    其實這里說的C++虛函數原理跟你之前了解的應該是一致的,只是很難技術細節你沒有想過而已,但不管理怎么樣,我們一起學習吧。

    后繼再跟大家分析,菱形繼承和虛繼承場景下,虛函數的技術細節。

    • 膽識與智慧:?博主,請問我可以轉載你的這篇文章嗎?(1個月前#2樓)查看回復(1)

    • Q943381546:?看了你的文檔收獲很多,個人認為有點小缺陷。“以這兩行代碼為例 derive *d = new derive(); d->g2(); 其實,不管derive是否有重寫g2函數,都是通過base2的虛函數表找出來的。”如果是重新函數,根據c++函數查找優先級,找到的是d的g2函數,應該是用d指針直接調用g2,不會退化到父類指針。(10個月前#1樓)查看回復(1)

    總結

    以上是生活随笔為你收集整理的图解C++虚函数 虚函数表的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 最近中文在线观看 | 一本色道久久综合狠狠躁 | 不卡在线一区二区 | 国产黑丝在线 | 毛片无码免费无码播放 | 美女视频久久久 | 99热这里只有精 | 人妻巨大乳hd免费看 | 老汉色老汉首页av亚洲 | 上原亚衣在线 | 黄色激情在线观看 | 成人欧美视频 | 亚洲1页| 草民午夜理伦三级 | 婷婷六月网 | 91亚洲精品乱码久久久久久蜜桃 | 日韩欧美高清一区 | 欧亚av在线 | 长腿校花无力呻吟娇喘的视频 | 日p视频在线观看 | 爱吃波客今天最新视频 | 黄色污网站在线观看 | 国产亚洲成av人在线观看导航 | 国产三级全黄裸体 | 色女孩综合 | 日韩一区二区三区四区在线 | 国产丝袜美腿一区二区三区 | 亚洲精品乱码久久久久久不卡 | 婷婷二区 | 国产精品99精品 | 久久精品中文闷骚内射 | 欧美一区二区三区精品 | 91久久精品一区二区三区 | 国产午夜在线视频 | 少妇高潮一区二区三区99小说 | 日批视频免费观看 | 羞辱狗奴的句子有哪些 | 久久成年视频 | 伊人久久超碰 | 一区二区三区四区中文字幕 | 欧美三日本三级少妇三级99观看视频 | 久久av资源 | 波多野结衣av在线播放 | 亚洲免费一区二区 | 国产睡熟迷奷系列精品视频 | 丰满人妻av一区二区三区 | 欧美成人自拍视频 | 亚洲黄av| 欧美成人午夜77777 | 国产成人啪精品午夜在线观看 | 在线尤物| 欧美特一级片 | 99国产超薄肉色丝袜交足 | 国产 中文 字幕 日韩 在线 | 欧美韩国日本一区 | 夜色一区二区三区 | 在线免费h | 亚洲综合视频一区 | www.五月婷婷.com | 波多野结衣一区二区三区高清av | 成人aaaa | 国产做爰免费视频观看 | 亚洲精品成人无码熟妇在线 | 伊人久久影视 | 成人免费视频一区 | 黄色片大全 | 黄色成人在线 | 亚洲av午夜精品一区二区三区 | 男人天堂亚洲天堂 | 午夜视频久久 | 欧美日本韩国一区二区 | 黄色一节片 | 337p粉嫩色噜噜噜大肥臀 | a级免费视频 | 国产91在线播放九色 | 手机在线免费av | 色欲无码人妻久久精品 | 日本激情一区二区三区 | 四虎在线免费 | 男生草女生视频 | 成人h网站 | 寡妇一级片 | 精品国产一区二区三区性色 | 色网网站 | 久久在线电影 | 久久视频在线播放 | 香港三级网站 | 超清av| 久草日韩 | 黄色片免费看 | 欧美日韩一区二区视频在线观看 | 国产91一区二区三区 | 中文字幕一区av | 亚洲熟妇无码乱子av电影 | 激情综合五月天 | 亚洲涩情| 我的好妈妈在线观看 | 蜜桃tv一区二区三区 | 亚洲黄色片子 |