C++ dtor 和虚表指针的一个讨论
#include <cstdio>
struct Base {
int vb;
virtual ~Base() {
puts("Base::~Base");
test(0);
}
virtual void test(int) { puts("Base::test"); }
// error!
// virtual void test(int) = 0;
};
struct Derived : public Base {
int vd;
~Derived() override {
puts("Derived::~Derived");
test(0);
}
void test(int) override { puts("Derived::test"); }
};
int main() {
Base *a = new Derived();
a->test(1);
puts("");
a->~Base();
puts("");
a->test(1);
return 0;
}
输出:
Derived::test
Derived::~Derived
Derived::test
Base::~Base
Base::test
Base::test
Base 析构的时候调用 test 的流程:
Derived::~Derived(Derived *this) {
this->vptr = vtable_for_Derived+16; // *(void *)this = offset_vtable;
// actual user defined Derived::~Derived
Base::Base(*this);
}
Base::~Base(Base *this) {
this->vptr = vtable_for_Base+16;
// actual user defined Base::~Base
}
vptr 是在 dtor 开始的时候设置的,所以 Base::test(int)
不能是纯虚函数。
多重继承的情况类似,假设 Derived
继承 Base1
, Base2
, Base3
,Base*
类中有一个 int64_t
成员变量:
Derived 内存布局:
+-------+------+-+
| Base1 | vptr |0|
| +------+-+
| | var |1|
+-------+------+-+
| Base2 | vptr |2|
| +------+-+
| | var |3|
+-------+------+-+
| Base3 | vptr |4|
| +------+-+
| | var |5|
+-------+------+-+
|vptr |6|
+--------------+-+
|var |7|
+--------------+-+
Derived::~Derived(Derived *this) {
// vptr_Base1 = 0;
// vptr_Base2 = 2 * 8;
// vptr_Base3 = 4 * 8;
this->vptr_Base1 = vtable_for_Derived+16;
this->vptr_Base2 = vtable_for_Derived_thunk_1+16;
this->vptr_Base3 = vtable_for_Derived_thunk_2+16;
// actual user defined Derived::~Derived
Base3::~Base3(this + offset_vptr_Base3);
Base2::~Base2(this + offset_vptr_Base2);
Base1::~Base1(this + offset_vptr_Base1);
}
Base2
, Base3
对应的 vptr 指向各自的 virtual thunk ,其中的函数只是实际函数的跳板:
.data:3C48 public _ZTV7Derived ; weak
.data:3C48 ; `vtable for'Derived
.data:3C48 _ZTV7Derived dq 0 ; offset to this
.data:3C50 dq offset _ZTI7Derived ; `typeinfo for'Derived
.data:3C58 off_3C58 dq offset _ZN7DerivedD2Ev
.data:3C58 ; DATA XREF: Derived::~Derived()+C↑o
.data:3C58 ; Derived::Derived(void)+38↑o
.data:3C58 ; Derived::~Derived()
.data:3C60 dq offset _ZN7DerivedD0Ev ; Derived::~Derived()
.data:3C68 dq offset _ZN7Derived4testEi ; Derived::test(int)
.data:3C70 dq -16 ; offset to this
.data:3C78 dq offset _ZTI7Derived ; `typeinfo for'Derived
.data:3C80 off_3C80 dq offset _ZThn16_N7DerivedD1Ev
.data:3C80 ; DATA XREF: Derived::~Derived()+1A↑o
.data:3C80 ; Derived::Derived(void)+46↑o
.data:3C80 ; `non-virtual thunk to'Derived::~Derived()
.data:3C88 dq offset _ZThn16_N7DerivedD0Ev ; `non-virtual thunk to'Derived::~Derived()
.data:3C90 dq offset _ZThn16_N7Derived4testEi ; `non-virtual thunk to'Derived::test(int)
.data:3C98 dq -32 ; offset to this
.data:3CA0 dq offset _ZTI7Derived ; `typeinfo for'Derived
.data:3CA8 off_3CA8 dq offset _ZThn32_N7DerivedD1Ev
.data:3CA8 ; DATA XREF: Derived::~Derived()+29↑o
.data:3CA8 ; Derived::Derived(void)+55↑o
.data:3CA8 ; `non-virtual thunk to'Derived::~Derived()
.data:3CB0 dq offset _ZThn32_N7DerivedD0Ev ; `non-virtual thunk to'Derived::~Derived()
.data:3CB8 dq offset _ZThn32_N7Derived4testEi ; `non-virtual thunk to'Derived::test(int)