C++虚函数表的内容分析

这篇文章继续分析C++虚函数表的内容,以及它的工作原理,即用户代码如何访问虚函数表的内容。

下面C++代码定义了一个类AAAA,main()函数new了一个对象,然后delete对象,我们按照调用顺序分析虚函数表的建立,关联等等操作。

#include <stdio.h>
#include <string>

class AAAA {
private:
    long l;
public:
    virtual void foo() {}
    virtual ~AAAA() {}
};

int main(int argc, char * argv[]) {
    AAAA * a = new AAAA();

    delete a;
    return 0;
}

从main()函数入口,主要有两条指令new一个AAAA对象,然后删除这个对象。

AAAA * a = new AAAA()

new指令生成的汇编指令如下:


    movl    $16, %edi
    call    _Znwm                         # operator new(unsigned long)
    movq    %rax, %rbx
    movq    %rbx, %rax
    movq    $0, (%rax)                #set instance buffer to 0
    movq    $0, 8(%rax)              # set instance buffer to 0
    movq    %rax, %rdi                # move instance pointer to %rdi for calling
    call    _ZN4AAAAC1Ev         # AAAA::AAAA()

主要有个三块功能,1. new一个16字节的内存,2. 内存初始化成0,3. 调用构造函数AAAA::AAAA(),即_ZN4AAAAC1Ev。
我们再看构造函数AAAA::AAAA()的代码:

_ZN4AAAAC1Ev:               # AAAA::AAAA()
    pushq   %rbp
    movq    %rsp, %rbp
    movq    %rdi, -8(%rbp)
    movq    -8(%rbp), %rax
    movq    $_ZTV4AAAA+16, (%rax)
    leave
    ret

在C++源代码里面,我们并没有为AAAA定义自己的构造函数,所以这个函数是缺省的构造函数,主要功能就一句话,把$_ZTV4AAAA+16的值赋值到对象实例的前8个字节(movq $_ZTV4AAAA+16, (%rax))。

再来看_ZTV4AAAA+16是个什么内容:

_ZTV4AAAA:                                              # vtable for AAAA
    .quad   0
    .quad   _ZTI4AAAA                               # typeinfo for AAAA
    .quad   _ZN4AAAA3fooEv
    .quad   _ZN4AAAAD1Ev
    .quad   _ZN4AAAAD0Ev
_ZTS4AAAA:                                             # typeinfo name for AAAA
    .string "4AAAA"
_ZTI4AAAA:                                              # typeinfo for AAAA
    .quad   _ZTVN10__cxxabiv117__class_type_infoE+16
    .quad   _ZTS4AAAA                             # typeinfo name for AAAA

上述代码都有编译器在翻译类AAAA的时候生成。我们看到_ZTV4AAAA是类AAAA的虚函数表地址,$_ZTV4AAAA+16指向的是虚函数AAAA:::foo()的地址;我们已经知道C++类对象内容的前八个字节是指向类虚函数表的指针,可是此时我们看到它并不是指向虚函数表首地址,而是指向首地址+16的一个偏移,为什么这样做呢?其实+16是第一个虚函数的地址,前面的16字节(+8字节指向类类型信息,+0我也不清楚其用处)保留属于C++类管理内部使用的,对用户而言可以隐藏,所以在使用者的角度看来,虚函数表就是按顺序从头开始排列的(+16偏移开始即可。

总结一句话,缺省构造函数就是把类的虚函数表地址写到类对象的前面8个字节地址。

下面我们看删除一个对象的函数

delete a;

delete指令生成汇编语言代码如下:

    movq    -24(%rbp), %rax
    movq    (%rax), %rax
    addq    $16, %rax
    movq    (%rax), %rdx
    movq    -24(%rbp), %rax
    movq    %rax, %rdi
    call    *%rdx

这段汇编代码的目的只有一个就是call到%rdx里面去,完成两件事,1.给%rdx找到正确的值,2.找到正确的函数参数。%rdx需要找到的值是析构函数的地址,参数当然是对象本身指针了。

从上述代码我们看到赋给%rdx的值是虚函数表+16的地址,看前面_ZTV4AAAA的定义,(+16)的地址就是指向第三个虚函数的地址,即_ZN4AAAAD0Ev


_ZN4AAAAD0Ev:           # AAAA::~AAAA()
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    %rdi, -8(%rbp)
    movq    -8(%rbp), %rax
    movq    %rax, %rdi
    call    _ZN4AAAAD1Ev
    movq    -8(%rbp), %rax
    movq    %rax, %rdi
    call    _ZdlPv
    leave
    ret

这个函数主要功能是调用另一个函数 _ZN4AAAAD1Ev

_ZN4AAAAD1Ev:           # AAAA::~AAAA()
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    %rdi, -8(%rbp)
    movq    -8(%rbp), %rax
    movq    $_ZTV4AAAA+16, (%rax)
    movq    -8(%rbp), %rax
    movq    %rax, %rdi
    call    _ZdlPv
    leave
    ret

函数_ZN4AAAAD1Ev是用户定义的析构函数,因为没有具体功能;也不清楚它具体要干什么,只看到最后它调用了一个delete函数。

上述代码可能比较复杂啰嗦,但是我们清楚了一个重要概念,即每一个多态类实例对象的起始地址都是一个指向虚函数表的指针,所有类的虚函数都在这个表中占用一列;这个地址的前面一个指针指向类的类型信息定义,从而从一个对象指针我们就能查到其类类型定义;这也是typeid和dyanmic_cast能够工作的原理。

1.jpg

最后说明一点在类_ZTV4AAAA的虚函数表中定义有两个析构函数,不知道为什么。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • C++虚函数 C++虚函数是多态性实现的重要方式,当某个虚函数通过指针或者引用调用时,编译器产生的代码直到运行时才...
    小白将阅读 1,742评论 4 19
  • 1.面向对象的程序设计思想是什么? 答:把数据结构和对数据结构进行操作的方法封装形成一个个的对象。 2.什么是类?...
    少帅yangjie阅读 4,999评论 0 14
  • 1. 结构体和共同体的区别。 定义: 结构体struct:把不同类型的数据组合成一个整体,自定义类型。共同体uni...
    breakfy阅读 2,123评论 0 22
  • 一个博客,这个博客记录了他读这本书的笔记,总结得不错。《深度探索C++对象模型》笔记汇总 1. C++对象模型与内...
    Mr希灵阅读 5,583评论 0 13
  • 厨艺这种东西还真需要熟能生巧,走不来捷径啊。 学习吧,方法真的是可以很多,执行过程中屡屡受挫,这些年越发不能控制自...
    好大一只虫阅读 175评论 0 0