我在看这篇文章,它说“在进入基类析构函数时,对象变成基类对象,C++ 的所有部分——虚函数、dynamic_casts 等——都以这种方式对待它。” 这是否意味着 vptr 在销毁过程中发生了变化?这是怎么发生的?
问问题
2080 次
2 回答
12
在所有使用虚函数表的实现(即所有当前的 C++ 实现)中,答案是肯定的,对vptr
正在执行的析构函数类型的更改。原因是标准要求被破坏的对象的类型是被执行的析构函数的类型。
如果您有 B、D、MD 三种类型(基、派生、最派生)的层次结构,并且您实例化并销毁类型为 的对象MD
,则在执行对象的类型时为,但当隐式调用基析构函数时执行时,对象的运行时类型必须为. 这是通过更新.MD::~MD()
MD
D
vptr
于 2011-10-27T13:55:32.210 回答
6
迂腐的 C++ 答案当然是,“标准没有说明 vtbls 或多态是如何实现的。”
但是,实际上,是的。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
dtor 的左大括号处设置一个断点。
当您运行此代码时,它在左大括号处中断(就在构造函数的主体开始执行之前),您可以查看 vptr:
此外,您可以查看Poly
dtor 的反汇编:
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_
,这会在调试器中生成运行时断言错误:
于 2011-10-27T14:06:28.017 回答