我在看这篇文章,里面说:“当进入基类析构函数时,对象成为一个基类对象,并且C++中的所有部分——虚函数、dynamic_cast等——都会以这种方式处理它。” 这是否意味着在销毁过程中vptr已经改变了?那是如何发生的呢?
我在看这篇文章,里面说:“当进入基类析构函数时,对象成为一个基类对象,并且C++中的所有部分——虚函数、dynamic_cast等——都会以这种方式处理它。” 这是否意味着在销毁过程中vptr已经改变了?那是如何发生的呢?
vptr
会被更改为正在执行的析构函数类型的类型。原因是标准要求正在销毁的对象的类型与正在执行的析构函数的类型相同。MD
类型的对象,在执行MD::~MD()
时对象的类型是MD
,但当隐式调用基类析构函数时,对象的运行时类型必须是D
。这是通过更新vptr
来实现的。当然,严格的C++回答是:“标准并没有关于虚函数表或多态实现的规定。”
但实际上,在实践中,是的。在基类析构函数开始执行之前,vtbl已经被修改。
编辑:
以下是我使用MSVC10来展示这个过程的代码:
#include <string>
#include <iostream>
using namespace std;
class Poly
{
public:
virtual ~Poly();
virtual void Foo() const = 0;
virtual void Test() const = 0 { cout << "PolyTest\n"; }
};
class Left : public Poly
{
public:
~Left()
{
cout << "~Left\n";
}
virtual void Foo() const { cout << "Left\n"; }
virtual void Test() const { cout << "LeftTest\n"; }
};
class Right : public Poly
{
public:
~Right() { cout << "~Right\n"; }
virtual void Foo() const { cout << "Right\n"; }
virtual void Test() const { cout << "RightTest\n"; }
};
void DoTest(const Poly& poly)
{
poly.Test();
}
Poly::~Poly()
{ // <=== BKPT HERE
DoTest(*this);
cout << "~Poly\n";
}
void DoIt()
{
Poly* poly = new Left;
cout << "Constructed...\n";
poly->Test();
delete poly;
cout << "Destroyed...\n";
}
int main()
{
DoIt();
}
Poly
析构函数的左花括号处设置一个断点。Poly
析构函数的反汇编代码:Poly::~Poly()
{
000000013FE33CF0 mov qword ptr [rsp+8],rcx
000000013FE33CF5 push rdi
000000013FE33CF6 sub rsp,20h
000000013FE33CFA mov rdi,rsp
000000013FE33CFD mov ecx,8
000000013FE33D02 mov eax,0CCCCCCCCh
000000013FE33D07 rep stos dword ptr [rdi]
000000013FE33D09 mov rcx,qword ptr [rsp+30h]
000000013FE33D0E mov rax,qword ptr [this]
000000013FE33D13 lea rcx,[Poly::`vftable' (13FE378B0h)]
000000013FE33D1A mov qword ptr [rax],rcx
DoTest(*this);
000000013FE33D1D mov rcx,qword ptr [this]
000000013FE33D22 call DoTest (13FE31073h)
cout << "~Poly\n";
000000013FE33D27 lea rdx,[std::_Iosb<int>::end+4 (13FE37888h)]
000000013FE33D2E mov rcx,qword ptr [__imp_std::cout (13FE3C590h)]
000000013FE33D35 call std::operator<<<std::char_traits<char> > (13FE3104Bh)
}
000000013FE33D3A add rsp,20h
000000013FE33D3E pop rdi
000000013FE33D3F ret
跨过下一行,进入析构函数体内,再次查看vptr:
现在当我们从析构函数体内调用
DoTest
时,vtbl已经被修改为指向purecall_
,这将在调试器中生成运行时断言错误:
Poly
),它不使用动态分派,而是使用静态分派。要修复测试,您可以创建一个函数,该函数接受Poly&
并对其调用Test
,从而强制分派。 - David Rodríguez - dribeas
vptr
。 - David Rodríguez - dribeas