10

我在看这篇文章,它说“在进入基类析构函数时,对象变成基类对象,C++ 的所有部分——虚函数、dynamic_casts 等——都以这种方式对待它。” 这是否意味着 vptr 在销毁过程中发生了变化?这是怎么发生的?

4

2 回答 2

12

在所有使用虚函数表的实现(即所有当前的 C++ 实现)中,答案是肯定的,对vptr正在执行的析构函数类型的更改。原因是标准要求被破坏的对象类型是被执行的析构函数的类型。

如果您有 B、D、MD 三种类型(基、派生、最派生)的层次结构,并且您实例化并销毁类型为 的对象MD,则在执行对象的类型时,但当隐式调用基析构函数时执行时,对象的运行时类型必须为. 这是通过更新.MD::~MD() MD Dvptr

于 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();
}

现在,在Polydtor 的左大括号处设置一个断点。

当您运行此代码时,它在左大括号处中断(就在构造函数的主体开始执行之前),您可以查看 vptr:在此处输入图像描述

此外,您可以查看Polydtor 的反汇编:

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 回答