Меняется ли vptr при уничтожении? - PullRequest
9 голосов
/ 27 октября 2011

Я просматривал эту статью , и там говорится: «При входе в деструктор базового класса объект становится объектом базового класса, а все части C ++ - виртуальные функции, dynamic_casts и т. Д. - относиться к этому так. Значит ли это, что vptr изменился при уничтожении? Как это происходит?

Ответы [ 2 ]

11 голосов
/ 27 октября 2011

Во всех реализациях, которые используют таблицы виртуальных функций (то есть во всех текущих реализациях C ++), ответ да, vptr меняется на тип выполняемого деструктора.Причина в том, что стандарт требует, чтобы тип разрушаемого объекта был типом разрушаемого деструктора.

Если у вас есть иерархия трех типов B, D, MD (базовый, производный, наиболее производный) и вы создаете и уничтожаете объект типа MD, тогда при выполнении MD::~MD() тип объекта равен MD, но при неявном вызове базыдеструктор выполняется, тип времени выполнения объекта должен быть D.Это достигается путем обновления vptr.

6 голосов
/ 27 октября 2011

Конечно, педантичный ответ 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: enter image description here

Также вы можете просмотреть разборку для 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:

enter image description here Теперь, когда мы вызываем DoTest из тела деструктора, vtblуже был изменен, чтобы указывать на purecall_, что генерирует ошибку подтверждения времени выполнения в отладчике:

...