I was looking at this article, and it says "Upon entry to the base class destructor, the object becomes a base class object, and all parts of C++—virtual functions, dynamic_casts, etc.—treat it that way." Does this mean that the vptr has changed during destruction? How does that happen?
-
2This is all very implementation-specific. – Fred Foo Oct 27 '11 at 13:51
-
5@larsmans: That is rarely implementation-specific, unless you are considering implementations that do not use virtual tables, of which there are none. In all implementations that use virtual functions, the `vptr` must be changed during destruction. – David Rodríguez - dribeas Oct 27 '11 at 13:56
-
4@Larsmans, I think it's safe to pretend that any question that mentions vtables and vptrs is prefaced with the qualifier "In implementations that use vtables and vptrs, ... ." Then we don't have to keep seeing pedantic comments pointing out that vtables and vptrs are an implementation detail not required by the standard. – Rob Kennedy Oct 27 '11 at 14:06
2 Answers
In all implementations that use virtual function tables (i.e. all current C++ implementations) the answer is yes, the vptr
changes to that of the type of the destructor that is being executed. The reason is that the standard requires that the type of the object being destructed is the type of the destructor being exectued.
If you have a hierarchy of three types B, D, MD (base, derived, most derived) and you instantiate and destroy an object of type MD
, then while executing MD::~MD()
the type of the object is MD
, but when the implicit call to the base destructor is executed, the runtime type of the object must be D
. This is achieved by updating the vptr
.

- 204,818
- 23
- 294
- 489
-
3The reason the type _must be_ right is that it's observable, by calling a virtual function during destruction. The right override must be called, and that might be different to the override that would have been called before destruction started, which means the vptr must have been updated. – Jonathan Wakely Dec 17 '12 at 14:46
-
1@JonathanWakely: Correct, without the *vptr* being updated during destruction a call to a virtual function could end up being dispatched to an overrider in a type whose destructor has already completed (This is the behavior in other languages, like Java). – David Rodríguez - dribeas Dec 17 '12 at 18:28
-
-
2@hl3mukkel: Yes, except that in the opposite direction (from base to most derived) – David Rodríguez - dribeas Oct 22 '17 at 22:04
The pedantic C++ answer is, of course, "The Standard doesn't say anything about vtbls or how polymorphism is implemented."
However, practically speaking, yes. The vtbl is modified before the body of the base class' destructor begins execution.
EDIT:
Here is how I used MSVC10 to see this happen for myself. First, the test code:
#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();
}
Now, set a breakpoint at the opening brace for the Poly
dtor.
When you run this code at it breaks on the opening brace (just before the body of the constructor begins executing), you can take a peek at the vptr:
Also, you can view the disassembly for the 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
Step over the next line, in to the body of the destructor, and take another peek at the vptr:
Now when we call
DoTest
from within the body of the destructor, the vtbl has already been modified to point to purecall_
, which generates a runtime assertion error in the debugger:

- 99,718
- 31
- 186
- 324
-
3I don't think that the test is conclusive, and if it were it would point to utter weirdness, where the vtable mechanism would dispatch in the destructor to a function whose pointer was never in the vtable (before destruction started). The problem with this particular test is that you are calling the virtual pointer directly from the destructor, and at that point because the compiler knows the exact type of the object (`Poly`) it is not using dynamic dispatch, but rather static dispatch. To fix the test, you can create a function that takes `Poly&` and calls `Test` on that, forcing the dispatch – David Rodríguez - dribeas Oct 27 '11 at 14:20
-
As vtable is implementation specific, maybe simply to look at *reinterpret_cast
(this) in destructors? :) – user396672 Oct 27 '11 at 14:40