前言
笔者在前一篇文章 理解C++菱形继承 中分享了一些关于cpp
菱形继承的理解。但是对于cpp
虚函数的理解还有些许的模糊,知道一些内容,但没有完全详尽。
接下来笔者将根据上篇文章的部分示例代码验证一些关于虚函数调用以及类调用的一些深度理解。
概念理解
笔者的理解如下:
cpp
虚函数地址存在于虚函数表中,含有虚函数的类其内部默认存在一个虚函数表指针,所以:这种类的内存占用大小
=虚函数表指针占用内存大小
+数据成员占用内存大小
+内存对齐增加的内存大小
。- 这个原理,其实笔者是知道的。但是,笔者对于
类对象地址
和虚函数表指针
及虚函数地址
之间如何转换的有一点模糊。这将在后续验证。
- 这个原理,其实笔者是知道的。但是,笔者对于
- cpp
对象函数调用
时,this指针
将作为第一个函数参数
传递到函数
中。这是编译器的默认实现,并不会显式体现出来。- 思考
c
和cpp
函数的区别,即调用对象函数
该如何实现。
- 思考
验证
将上篇文章中的部分示例代码调试反汇编
后,按步详细解析汇编代码,便对上面的理解有了完整清晰的理解。原部分cpp
代码:
int main(int argc, char* argv[]) {
DerivedB *d = new DiamondDerived();
d->VirtualFunc("called_from_main");
delete d;
return 0;
}
可以在软件visual studio
中点击调试
– 窗口
– 反汇编
。
以下是反汇编的片段,笔者已按步注释,直接看重点:
- 汇编常识
rax
寄存器一般存储返回值。rcx
rdx
寄存器一般存储入参。rbp
栈底指针寄存器,rsp
栈顶指针寄存器。
反汇编解析:
d->VirtualFunc("called_from_main");
;; 下面是上面这句cpp语句对应的汇编代码
; 取字符串常量call_from_main的地址给rdx寄存器
00007FF7AAFFAEA0 lea rdx,[string "called_from_main" (07FF7AB000438h)]
; 将取栈底偏移128字节的内存地址给rcx寄存器,即临时变量,当入参传递给下列的basic_string构造函数。
; 其实就是临时变量string("called_from_main")的初始化,这是它的内存地址,可看做它的this指针。
00007FF7AAFFAEA7 lea rcx,[rbp+128h]
; 调用basic_string构造函数
00007FF7AAFFAEAE call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF7AAFF12C6h)
; nop指令,不做任何操作,占位,用途是内存对齐。
00007FF7AAFFAEB3 nop
; 取内存地址d的8字节数据给rax寄存器
00007FF7AAFFAEB4 mov rax,qword ptr [d]
; 取rax存储值对应的地址的8字节数据给rax寄存器
00007FF7AAFFAEB8 mov rax,qword ptr [rax]
; 上面这两部理解为:
; 第一步先将内存地址d存储的8字节数据转换为this指针,赋值给rax。
; 第二部将this指针指向的8字节数据转换为另一个指针,其实是虚函数表指针。
; 取栈底往上偏移128字节对应的地址赋值给rdx
00007FF7AAFFAEBB lea rdx,[rbp+128h]
; 取内存地址d的8字节数据给rcx,即rcx存储this指针
00007FF7AAFFAEC2 mov rcx,qword ptr [d]
; 调用rax地址偏移8字节对应内存的函数,就是调用虚函数表指针偏移8字节后的函数,即某个虚函数
00007FF7AAFFAEC6 call qword ptr [rax+8]
; 占位符,内存对齐。
00007FF7AAFFAEC9 nop
; 将栈底偏移128字节的内存地址赋值给rcx。
; 即前面的临时变量。作为下面调用basic_string析构函数的this指针。
00007FF7AAFFAECA lea rcx,[rbp+128h]
; 调用basic_string的析构函数,释放临时变量
00007FF7AAFFAED1 call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (07FF7AAFF1136h)
结尾
- 行文至此,关于
虚函数调用过程深度详解
的分享就结束了。希望能帮助到你。 - 行文如有错误,欢迎指正。
- 如果觉得还不错,你的
点赞关注加收藏
便是笔者继续更新
的最大动力哦^_^
。 - 关联文章感兴趣可移步。