Note: My first answer was so off-base that I deleted it. It was so far off-base that someone should have down voted my response. This is another try.
In the opening post, master_latch asked:
How can the compiler know which derived class destructor to invoke from the base destructor?
How this happens is implementation specific.
Why this has to happen is "because the standard says so." Here's what the standard says:
C++11 12.4 paragraph 5:
After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant members, the destructors for X’s direct base classes and, if X is the type of the most derived class, its destructor calls the destructors for X’s virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor. A return statement in a destructor might not directly return to the caller; before transferring control to the caller, the destructors for the members and bases are called. Destructors for elements of an array are called in reverse order of their construction.
C++11 12.4 paragraph 10:
In an explicit destructor call, the destructor name appears as a ~ followed by a type-name or decltype-specifier that denotes the destructor’s class type. The invocation of a destructor is subject to the usual rules for member functions, ...
The example code in C++11 12.4 paragraph 10 indicates the intent of the above:
struct B {
virtual ~B() { }
};
struct D : B {
~D() { }
};
D D_object;
B* B_ptr = &D_object;
void f() {
D_object.B::~B(); // calls B’s destructor
B_ptr->~B(); // calls D’s destructor
...
}
master_latch, your example of using b->~Base();
is identical to the second call in the example code. Think of b->~Base();
as if it meant b->__destruct_me()
. It is in a sense no different than a call to any other virtual function.
A compliant implementation has to do this because "because the standard says so." How an implementation does it? The standard doesn't say. (That's good requirementese, by the way. Say what has to be done, but don't say how to do it.)
Most implementations (every implementation I have poked into) do this by generating multiple functions for a destructor. One function implements the body of the destructor as specified by the programmer. A wrapper destructor executes this body of the destructor function, then destroys non-static data members in reverse order of construction, and then invokes parent class destructors. That classes can virtually inherit from some parent class adds another twist. This means that a third destructor function for a given class might be needed.
So how does an implementation know that b->~Base()
should call the wrapper destructor for class Derived
? Dynamically casting a pointer to a polymorphic class to a void* pointer yields a pointer to the most derived object.
C++11 5.2.7 paragraph 7:
If T
is “pointer to cv void
,” then the result is a pointer to the most derived object pointed to by v
. Otherwise, a run-time check is applied to see if the object pointed or referred to by v
can be converted to the type pointed or referred to by T
.
In other words, dynamically casting a polymorphic pointer to void*
yields a pointer to the object as it was declared or allocated. The virtual table (not a part of the standard) dictates how to find the destructor. The implementation ensures that the pointer to the virtual table can be determined from a void*
pointer to the most derived object. This is what lets the implementation know which destructor to call. From that point on, the pointer to that most derived object is no longer a void*
pointer.