Skip to content

Commit 28bd88d

Browse files
committed
Update Object Model
1 parent 431679a commit 28bd88d

File tree

2 files changed

+5
-8
lines changed

2 files changed

+5
-8
lines changed

docs/cpp-object-model.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,9 @@ MyDerived (0x0x6677f70) 0 nearly-empty
404404
## 虚基类的析构函数
405405
通过上面的打印信息发现一个细节,MyDerived明明没有定义析构函数,但还是帮我们生成了虚析构函数,而且打印位置还在`dfunc1`的前面。说明这个析构函数是从基类里面来的,当基类虚函数表复制过来的时候,发现有析构函数,就自动将其替换为了派生类的析构函数。
406406

407-
这可能也与虚基类的析构函数必须是虚函数有关,也与虚函数在机器码中的调用方式有关。虚函数调用被编译为机器码时,是通过对象的地址偏移调用的。当我们有一个基类类型的多态对象被delete时,如果你没有把基类的析构函数定义为虚函数,则会被编译为调用其非虚函数的析构函数,因为这个变量类型是基类类型,而多态是运行时特性,编译器无法判断这个对象是否是多态的,只能根据其类型来判断
407+
这可能也与虚基类的析构函数必须是虚函数有关,也与虚函数在机器码中的调用方式有关。虚函数调用被编译为机器码时,是通过对象的地址偏移调用的。当我们有一个基类类型的多态对象被delete时,如果你没有把基类的析构函数定义为虚函数,则会被编译为调用其非虚函数的析构函数,因为这个变量类型是基类类型,而多态是运行时特性,编译器无法判断这个对象是否是多态的,只能调用其类型的析构函数
408408

409-
而如果你把析构函数定义为虚函数,则会被编译为通过从地址偏移来调用 [[ref]](https://www.cnblogs.com/chio/archive/2007/09/07/886337.html)呈现的结果就是从运行时的虚函数表里找到析构函数的地址,然后将其调用。从上面的打印信息可以看出,基类和派生类的虚函数表里呈现覆盖关系的函数的偏移都是相同的,当一个多态的基类类型对象调用析构函数时通过偏移找到的将是派生类的析构函数
409+
而如果你把析构函数定义为虚函数,则会被编译为通过从地址偏移来调用 [[ref]](https://www.cnblogs.com/chio/archive/2007/09/07/886337.html)呈现的结果就是从运行时的虚函数表里通过偏移得到析构函数的地址,然后将其调用。
410410

411411
## 多继承
412412

@@ -457,9 +457,9 @@ MySub (0x0x720da10) 0
457457

458458
- 可以发现有两个vptr,一个偏移16,一个偏移了64。对照Vtable,第一个偏移16就是指向MyBase的第一个虚函数,第二个偏移64指向的是MyDerived的第一个虚函数。
459459
- MyDerived::dfunc1的前面两个东西已经说过了,其中offset=-16,其实是指MyDerived的虚函数表指针相对于MySub对象的指针偏移了16。为什么偏移了16,下面讲。
460-
- size=32,构成是 vptr MyBase(8字节) + MyBase.dataBase(4字节整型,但内存对齐到8字节) + vptr MyDerived(8字节) + MyDerived.dataDerived(对齐到8字节)。其中vptr MyDerived相对于首部的偏移为16。
461-
462-
## 引用
460+
- size=32,构成是 vptr MyBase(8字节) + MyBase.dataBase(4字节整型,但内存对齐到8字节) + vptr MyDerived(8字节) + MyDerived.dataDerived(对齐到8字节)。其中vptr MyDerived相对于首部的偏移为16字节。
463461

462+
## 参考
463+
- [深入分析C++虚函数表](https://jocent.me/2017/08/07/virtual-table.html)
464464
- [C++对象模型](https://miaoerduo.com/2023/01/19/cpp-object-model/)
465465
- 《Inside the C++ Object Model》

docs/cpp-ploy.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,3 @@ https://www.zhihu.com/question/35632207/answer/63936329)
146146
答案是可以调用,但最好不要。构造函数调用时,指向虚函数表的指针还没被初始化,虽然确实可以调用,但只是作为成员函数,如果存在继承关系,这时派生类还没有构造,它不会调用到派生类的实现版本;
147147

148148
而对于析构函数,派生类析构后,基类调用的虚函数到底是自己的还是派生类的呢?若是调用基类实现则与运行时的版本不同,若调用派生类的,可能会访问到已回收的属性,所以最好不要这么干。
149-
150-
## 参考
151-
- [深入分析C++虚函数表](https://jocent.me/2017/08/07/virtual-table.html)

0 commit comments

Comments
 (0)