Consider this code:
class A {
public:
void fun() {}
};
class B : public A {
public:
void fun() {}
};
int main()
{
A *p = new B;
delete p;
}
Classes A and B are not polymorphic, and neither class declares a destructor. If I compile this code with g++ -Wall, the GCC compiler happily compiles the code.
But if I add virtual to void fun() in A, the compiler issues this warning: "deleting object of polymorphic class type ‘A’ which has non-virtual destructor might cause undefined behavior".
I'm quite aware of the dangers of using non-virtual destructors. But the code above makes me wonder about two things:
Why do I need to write an empty virtual destructor in the base class when I'm not using destructors at all?
Why is the empty virtual destructor not required if the base class contains no other virtual functions?
EDIT
It appears that I need to clarify the thing that bothers me:
The above code declares no destructors.
If I declare a virtual function, the compiler complains about the missing virtual destructor. My conclusion: If the class is polymorphic, I need to write a virtual destructor if delete p is to work correctly.
But if I declare no virtual function (as in the initial example above), the compiler does not complain about a missing virtual destructor. My conclusion: If the class is not polymorphic, I do not need to write a virtual desctructor, and delete p will work correctly anyway.
But that last conclusion sounds intuitively wrong to me. Is it wrong? Should the compiler have complained in both cases?
Following up on PaulMcKenzie's and KerrekSB's comments, here is the answer to the two questions in the original post:
The class always has a destructor, even when the programmer doesn't explicitly write one. It is necessary to declare an empty virtual destructor in order to prevent the system from automatically generating a non-virtual one.
In the sample code provided, you do need a virtual destructor, even when there is no other virtual function in the class. The fact that GCC doesn't complain in this case is probably a bug in the compiler (or at least a shortcoming).
The background for this is found in §5.3.5 of the C++11 standard, which says, "if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined." (Italics mine.)
You are making an upcasting, in other words: a polymorphic use of the class B. If the class A has not virtual members, the compiler does not generate VTABLE for class A and it is not require a virtual destructor (note that your upcasting has no sense without polymorphism use). While if the class A declares virtual members, a VTABLE is generated by compiler, in this case you should provide a virtual destructor.
If your want polymorphic behaviour you need to define at least one virtual function in order compiler should generate v-table for your class.
Because C++ class contains two special functions (constructor and destructor) which used for every object it a good choice to make destructor virtual.
When you write delete p you really call a destructor for object associated with pointer p. If you not declare a destructor as virtual you've get error prone code.
Before your declare at least one of member function as virtual compiler did not expect that you intend using your class as polymorphic. In C++ phylosophy: "You should not pay for functionallity that you will never use". E.g. in simple case destructor should not be virtual.
Related
Everyone knows that the desructor of base class usually has to be virtual. But what is about the destructor of derived class? In C++11 we have keyword "override" and ability to use the default destructor explicitly.
struct Parent
{
std::string a;
virtual ~Parent()
{
}
};
struct Child: public Parent
{
std::string b;
~Child() override = default;
};
Is it correct to use both keywords "override" and "=default" in the destructor of Child class? Will compiler generate correct virtual destructor in this case?
If yes, then can we think that it is good coding style, and we should always declare destructors of derived classes this way to ensure that base class destructors are virtual?
Is it correct to use both keywords "override" and "=default" in the destructor of Child class? Will compiler generate correct virtual destructor in this case?
Yes, it is correct. On any sane compiler, if the code compiles without error, this destructor definition will be a no-op: its absence must not change the behavior of the code.
can we think that it is good coding style
It's a matter of preference. To me, it only makes sense if the base class type is templated: it will enforce a requirement on the base class to have a virtual destructor, then. Otherwise, when the base type is fixed, I'd consider such code to be noise. It's not as if the base class will magically change. But if you have deadheaded teammates that like to change things without checking the code that depends on what they may be possibly breaking, it's best to leave the destructor definition in - as an extra layer of protection.
override is nothing more than a safety net. Destructor of the child class will always be virtual if base class destructor is virtual, no matter how it is declared - or not declared at all (i.e. using implicitly declared one).
According to the CppCoreGuidelines C.128 the destructor of the derived class should not be declared virtual or override.
If a base class destructor is declared virtual, one should avoid declaring derived class destructors virtual or override. Some code base and tools might insist on override for destructors, but that is not the recommendation of these guidelines.
UPDATE: To answer the question why we have a special case for destructors.
Method overriding is a language feature that allows a subclass or child class to provide a specific
implementation of a method that is already provided by one of its superclasses or parent classes. The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class.
In other words, when you call an overridden method only the last implementation of that method (in the class hierarchy) is actually executed while all the destructors (from the last child to the root parent) must be called to properly release all the resources owned by the object.
Thus we don't really replace (override) the destructor, we add additional one into the chain of object destructors.
UPDATE: This CppCoreGuidelines C.128 rule was changed (by 1448, 1446 issues) in an effort to simplify already exhaustive list of exceptions. So the general rule can be summarized as:
For class users, all virtual functions including destructors are equally polymorphic.
Marking destructors override on state-owning subclasses is textbook hygiene that you should all be doing by routine (ref.).
There is (at least) one reason for using override here -- you ensure that the base class's destructor is always virtual. It will be a compilation error if the derived class's destructor believes it is overriding something, but there is nothing to override. It also gives you a convenient place to leave generated documentation, if you're doing that.
On the other hand, I can think of two reasons not to do this:
It's a little weird and backwards for the derived class to enforce behavior from the base class.
If you define a destuctor in the header (or if you make it inline), you do introduce the possibility for odd compilation errors. Let's say your class looks like this:
struct derived {
struct impl;
std::unique_ptr<derived::impl> m_impl;
~derived() override = default;
};
You will likely get a compiler error because the destructor (which is inline with the class here) will be looking for the destructor for the incomplete class, derived::impl.
This is my round-about way of saying that every line of code can become a liability, and perhaps it's best to just skip something if it functionally does nothing. If you really really need to enforce a virtual destructor in the base class from the parent class, someone suggested using static_assert in concert with std::has_virtual_destructor, which will produce more consistent results, IMHO.
I think "override" is kind of misleading on destructor.
When you override virtual function, you replace it.
The destructors are chained, so you can't override destructor literally
The CPP Reference says that override makes sure that the function is virtual and that it indeed overrides a virtual function. So the override keyword would make sure that the destructor is virtual.
If you specify override but not = default, then you will get a linker error.
You do not need to do anything. Leaving the Child dtor undefined works just fine:
#include <iostream>
struct Notify {
~Notify() { std::cout << "dtor" << std::endl; }
};
struct Parent {
std::string a;
virtual ~Parent() {}
};
struct Child : public Parent {
std::string b;
Notify n;
};
int main(int argc, char **argv) {
Parent *p = new Child();
delete p;
}
That will output dtor. If you remove the virtual at Parent::~Parent, though, it will not output anything because that is undefined behavior, as pointed out in the comments.
Good style would be to not mention Child::~Child at all. If you cannot trust that the base class declared it virtual, then your suggestion with override and = default will work; I would hope that there are better ways to ensure that instead of littering your code with those destructor declarations.
Though destructors are not inherited there is clear written in the Standard that virtual destructors of derived classes override destructors of base classes.
From the C++ Standard (10.3 Virtual functions)
6 Even though destructors are not inherited, a destructor in a derived
class overrides a base class destructor declared virtual; see 12.4 and
12.5.
On the other hand there is also written (9.2 Class member)
8 A virt-specifier-seq shall contain at most one of each virt-specifier.
A virt-specifier-seq shall appear only in the declaration of a
virtual member function (10.3).
Though destructors are called like special member functions nevertheless they are also member functions.
I am sure the C++ Standard should be edited such a way that it was unambiguous whether a destructor may have virt-specifier override. At present it is not clear.
I was trying to familiarize myself with the OOP concepts but could not quite understand the concept of virtual.
One can create a virtual destructor but not a virtual constructor. Why?
How are virtual destructors handled internally? I mean the link Virtual Destructors illustrates the concept but my question is how the vptr of both the vtables (Derived and Base) are called? (In case of virtual member functions when such a scenario occurs generally the function that vptr of Derived class points to is only called)
Are there any other scenarios where one may need to use a virtual destructor?
Can anyone please help me understand the above concepts with links/examples?
First, a little about the difference between virtual functions and non-virtual functions:
Every non-virtual function-call that you have in your code can be resolved during compilation or linkage.
By resolved, we mean that the address of the function can be computed by the compiler or the linker.
So in the object code created, the function-call can be replaced with an op-code for jumping to the address of that function in memory.
With virtual functions, you have the ability to invoke functions which can be resolved only during runtime.
Instead of explaining it, let's run through a simple scenario:
class Animal
{
virtual void Eat(int amount) = 0;
};
class Lion : public Animal
{
virtual void Eat(int amount) { ... }
};
class Tiger : public Animal
{
virtual void Eat(int amount) { ... }
};
class Tigon : public Animal
{
virtual void Eat(int amount) { ... }
};
class Liger : public Animal
{
virtual void Eat(int amount) { ... }
};
void Safari(Animal* animals[], int numOfAnimals, int amount)
{
for (int i=0; i<numOfAnimals; i++)
animals[i]->Eat(amount);
// A different function may execute at each iteration
}
As you can probably understand, the Safari function allows you to be flexible and feed different animals.
But since the exact type of each animal is not known until runtime, so is the exact Eat function to be called.
The constructor of a class cannot be virtual because:
Calling a virtual function of an object is performed through the V-Table of the object's class.
Every object holds a pointer to the V-Table of its class, but this pointer is initialized only at runtime, when the object is created.
In other words, this pointer is initialized only when the constructor is called, and therefore the constructor itself cannot be virtual.
Besides that, there is no sense for the constructor to be virtual in the first place.
The idea behind virtual functions is that you can call them without knowing the exact type of the object with which they are called.
When you create an object (i.e., when you implicitly call a constructor), you know exactly what type of object you are creating, so you have no need for this mechanism.
The destructor of a base-class has to be virtual because:
When you statically allocate an object whose class inherits from the base-class, then at the end of the function (if the object is local) or the program (if the object is global), the destructor of the class is automatically invoked, and in turn, invokes the destructor of the base-class.
In this case, there is no meaning to the fact that the destructor is virtual.
On the other hand, when you dynamically allocate (new) an object whose class inherits from the base-class, then you need to dynamically deallocate (delete) it at some later point in the execution of the program.
The delete operator takes a pointer to the object, where the pointer's type may be the base-class itself.
In such case, if the destructor is virtual, then the delete operator invokes the destructor of the class, which in turn invokes the destructor of the base-class.
But if the destructor is not virtual, then the delete operator invokes the destructor of the base-class, and the destructor of the actual class is never invoked.
Consider the following example:
class A
{
A() {...}
~A() {...}
};
class B: public A
{
B() {...}
~B() {...}
};
void func()
{
A* b = new B(); // must invoke the destructor of class 'B' at some later point
...
delete b; // the destructor of class 'B' is never invoked
}
One can create a virtual destructor but not a virtual constructor. Why?
Virtual functions are dispatched according to the type of the object they're called on. When a constructor is called, there is no object - it's the constructor's job to create one. Without an object, there's no possibility of virtual dispatch, so the constructor can't be virtual.
How are virtual destructors handled internally?
The internal details of virtual dispatch are implementation-defined; the language doesn't specify the implementation, only the behaviour. Typically, the destructor is called via a vtable just like any virtual function.
how the vptr of both the vtables (Derived and Base) are called?
Only the most-derived destructor will be called virtually. All destructors, virtual or not, will implicitly call the destructors of all member and direct base-class subobjects. (The situation is slightly more complicated in the presence of virtual inheritance; but that's beyond the scope of this question).
Are there any other scenarios where one may need to use a virtual destructor?
You need one in order to support polymorphic deletion; that is, to be able to delete an object of derived type via a pointer to a base type. Without a virtual destructor for the base type, that's not allowed, and will give undefined behaviour.
because a Virtual function is invoked at runtime phase, however constructors are invoked at initialization phase, object is not constructed. So it's meaningless to have a virtual constructor.
a. the reason why only the base class desctructor is invoked in your link, the destructor is not marked as virtual, so the desctructor address is linked to Base class destructor at compile/link time, and obviously the type of the pointer is Base instead of Derived at compile time.
b. for why both of Base and Derived constructors are invoked after adding virtual to Base desctructor. It's same behavior like below:
Derived d; // when d exits the lifecycle, both Derived and Base's desctructor will be invoked.
Suppose when you have at least one virtual function, you should have a virtual desctructor.
One can create a virtual destructor but not a virtual constructor.
Why?
I'll try and explain this in layman's terms.
A class in c++ only exists after it's constructor completes. Each base class exists prior to initialisation of derived class and its members (vtable links included). Hence, having a virtual constructor does not make sense (since to construct, you need to know the type). Furthermore (in c++), calling virtual functions from a constructor does not work (as the derived class's vtable part has not been set up). If one thinks about it carefully, allowing virtual functions to be called from a contructor opens up a can of worms (such as what if virtual functions of derived classes are called prior to member initialization).
As far as destructors are concerned, at the point of destruction, the vtable is "intact", and we (c++ runtime) are fully aware of the type (so to speak). The destructor of the most derived part of the type is found (if virtual, through vtable), and therefore that destructor, and naturally that of all bases can be called.
How are virtual destructors handled internally? I mean the link
Virtual Destructors illustrates the concept but my question is how the
vptr of both the vtables (Derived and Base) are called?
Destructors are handled the same as normal virtual functions (that is, there addresses are looked up in a vtable if they are virtual at the expense of one (perhaps 2?) extra level/s of indirection). Furthermore, c++ guarantees that all base destructors shall execute (in opposite order of construction which relies on order of declaration) after completion of a derived destructor.
One can mimick/simulate virtual construction by using patterns such as the prototype pattern (or cloning), or by using factories. In such cases either an instance of the real type exists (to be used polymorphically), or a factory exists (deriving from abstract factory), that creates a type (via virtual function) based on some knowledge provided.
Hope this helps.
I assume we have a Base class A, and it's derived B.
1.: You can delete B via an A pointer, and then the correct method is to call the B destructor too.
However, you just can't say, that a B object should be created while you actually just call the A constructor. There is just not such a case.
You can say:
A* a = new B ();
or
B b;
But both directly call the B's constructor.
2.: Well, i am not entirely sure, but i guess it will iterate through the relevant part of the class hierarchy, and search for the closest call of the function. If a function is not virtual, it stop iterating and call it.
3.: You should always use virtual destructor, if you want to inherit something from that class. If it's a final class, you shouldn't.
I wasted a couple of days trying to discover why my derived virtual destructors were not being called before discovering the answer so hopefully I can save other a lot of grief with this reply.
I started using derived classes three and four levels deep in my project. Virtual functions seemed to work fine but then I discovered I had massive memory leaks because my destructors were not being called. No compiler or runtime error - the destructors just were not being called.
There is a ton of documentation and examples about this on the web but none of it was useful because my syntax was correct.
I decided that if the compiler wasn't going to call my destructors, I needed to create my own virtual destruct method to call. Then I got the compiler error that solved the problem - "class if a Forward Reference". Adding an include for the derived class header files in the base class solved the problem. The compiler needs the class definition to call the destructor!
I suggest when creating a new derived class, include the header file in the base and intermediate classes. Probably also a good idea to add conditional debug code to your destructors to check that they are bing called.
Bob Rice
I have a class declared as follow:
class TestFoo {
public:
TestFoo();
virtual void virtualFunction();
void nonVirtualFunction();
};
that I try to implement this way
TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}
which on compilation returns the error:
undefined reference to vtable for TestFoo
I tried :
TestFoo::TestFoo(){}
void TestFoo::nonVirtualFunction(){}
void TestFoo::virtualFunction(){}
which compiles ok which is consistent to the answers to these posts:
Undefined reference to vtable
undefined reference to vtable
What confuses me is that I thought the whole point of declaring a virtual function is that I would not need to define it. In this example, I am not planning to create any instance of TestFoo, but to create instances of (concrete) classes inheriting from TestFoo. But still, I want to define the function nonVirtualFunction for every subclasses of TestFoo.
Something I did not get right ?
Thanks !
the whole point of declaring a virtual function is that I would not
need to define it
Not quite, it says "I may want to replace the implementation of this function by something else in a derived class."
I may have misunderstood your question, but you seem to imply that you don't think you can define pure virtual member function in C++, which you can. You can declare one as follows.
virtual void virtualFunction() = 0;
Normally, a pure virtual function won't be defined, but of course you can. That says "There is no default implementation of this function because it won't always make sense, but I'll provide you with an implementation that you can opt into."
By the way, if a class has any virtual functions, you should also define a virtual destructor, as it is perfectly legal (and often recommended) to have a base class (smart) pointer to a derived class - without a virtual destructor, the object may not be deleted correctly.
... I thought the whole point of declaring a
virtual function is that I would not need to define it ...
For that facility you have a feature called pure virtual methods:
virtual void virtualFunction() = 0; // no linking error now
Note that, a virtual method cannot remain unimplemented. The reason is that for every virtual method declared inside a class body there has to be a vtable entry. Failing to find its body results in linking error.
Purpose of this restriction:
Unless a class is abstract - that is it has at least one virtual function - there is no way you can guarantee to the compiler that you are not going to declare an object of TestFoo. What happens when you do following:
DerivedOfTestFoo obj1;
TestFoo obj2 = obj1, *p = &obj2; // object slicing
p->virtualFunction(); // where is the body?
Other situation; in constructor there is no virtual mechanism:
TestFoo::TestFoo () {
this->virtualFunction(); // where is the body?
}
We can conclude that, compilers follow the rule, "Better to be safe than sorry". :)
Your description matches perfectly with the case of an abstract class. Declare your virtual function as:
virtual void VirtualFunction () = 0;
This means that you are not implementing the function in this class. As a result, the class becomes abstract. That is, no bare objects of this class can be instantiated.
Also, you should provide a virtual destructor.
Update: Some clarifications...
The language allows you to redefine a non-virtual function. Though, the wrong version might be called in some cases:
derived D; // rB is a reference to base class but it
base & rB=D; // points to an object of the derived class
rB.NonVirtualFunction (); // The base-class version is called
For this reason, redefining a non-virtual function is strongly discouraged nowadays. See Scott Meyers' "Effective C++, Third Edition: 55 Specific Ways to Improve Your Programs and Designs", item 36: "Never redefine an inherited non-virtual function."
See also item 7: "Declare destructors virtual in polymorphic base classes". An example:
base * pB = new derived;
delete pB; // If base's destructor is not virtual,
// ~derived() will not be called.
In case you wonder why isn't everything virtual by default, the reason is that calling a virtual function is slightly slower than calling a non-virtual one. Oh, and objects of classes with virtual functions occupy a few more bytes each.
If you want make this virtual function as pure virtual function,do not want to define it then, virtual void virtualFunction() = 0;
I read that that virtual destructors must be declared in classes that have virtual methods. I just cant understand why they must be declared virtual. I know why we need to have virtual destructors as from the following example. I just wanted to know why compilers dont manage virtual destructors for us. Is there something I need to know about working of virtual destructors ?
The following example shows that if destructors are not declared virtual the destructors of derived class are not called why is that ?
class Base
{
// some virtual methods
public:
Base()
{std::cout << "Base Constructor\n";}
~Base()
{std::cout << "Base De-structor\n";}
};
class Derived : public Base
{
public:
Derived()
{std::cout << "Der constructor\n";}
~Derived()
{ std::cout << "Der De-structor\n";}
} ;
void main()
{
Base *b = new Derived();
delete b;
}
I just wanted to know why compilers dont manage virtual destructors for us.
Because in C++, you pay for what you use. Having a virtual destructor by default involves the compiler adding a virtual table pointer to the class, which increases its size. This is not always desirable.
The following example shows that if destructors are not declared virtual the destructors of derived class are not called why is that ?
The example exibits undefined behavior. It's simply against the rules. The fact that not all destructors are called is just one possible manifestation. It could possibly crash.
Is there something I need to know about working of virtual destructors ?
Yes. They are required if you're deleting an object through a pointer to a base class. Otherwise it's undefined behavior.
5.3.5 Delete [expr.delete]
3) In the first alternative (delete object), if the static type of the object to be deleted is different from its
dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the
static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete
array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined. (emphasis mine)
I read that that virtual destructors must be declared in classes that have virtual methods.
"must" is too strong a word: "should" fits much better into that advise.
I just wanted to know why compilers dont manage virtual destructors for us.
C++ designers tried to avoid compiler doing things that you did not ask it to do only under the most extreme circumstances. Language designers recognized that the decision to make a class polymorphic should rest with the designer of the program, so they refused to re-assign this responsibility to the compiler.
The following example shows that if destructors are not declared virtual the destructors of derived class are not called why is that?
Because your code is invalid: by declaring the destructor of Derived non-virtual you made a promise to never destroy Derived through a pointer to Base; your main breaks this promise, invoking undefined behavior.
Note that by merely declaring your b variable with the exact type you would have avoided the problem associated with the non-virtual destructor (link to ideone). However, this leads to a rather shaky design, so you should avoid inheritance hierarchies with virtual functions and non-virtual destructors.
I read that that virtual destructors must be declared in classes that have virtual methods.
Yes. But that is an oversimplification.
Its not that a class with virtual methods needs a virtual destructor. But the way a class with virtual methods is used means that it will usually need a virtual destructor. A virtual destructor is ONLY needed if you delete an object via a pointer to its base class. The problem is that when an object has virtually methods you are usually working with a pointer to its base class even though the actual object is slightly different.
I just cant understand why they must be declared virtual.
It's not that they must. As explained above. This is a result of the usual usage patterns.
I just wanted to know why compilers dont manage virtual destructors for us.
Because it is not always needed. And the ethos of C++ is you don't have to pay for something you don't need it. If the compiler always added virtual destructors to a class with virtual methods then I would have to pay the price of using a virtual destructor even in situations I can prove in my code base that I don't need it.
Is there something I need to know about working of virtual destructors ?
Just that there is a slight cost to using them.
if destructors are not declared virtual the destructors of derived class are not called why is that ?
That is why we have virtual destructors to cause this behavior. If you need this behavior you need to add virtual destructors. But there are cases were virtual destructors may not be required which allows the user of this method not to pay the price.
I will type an example :
class A
{
public:
virtual ~A(){}
};
class B: public A
{
public:
~B()
{
}
};
int main(void)
{
A * a = new B;
delete a;
return 0;
}
Now in Above Example , destructors will be called recursively bottom to up .
My Question is how Compiler do this MAGIC .
There are two different pieces of magic in your question. The first one is how does the compiler call the final overrider for the destructor and the second one is how does it then call all the other destructors in order.
Disclaimer: The standard does not mandate any particular way of performing this operations, it only mandates what the behavior of the operations at a higher level are. These are implementation details that are common to various implementations, but not mandated by the standard.
How does the compiler dispatch to the final overrider?
The first answer is the simple one, the same dynamic dispatch mechanism that is used for other virtual functions is used for destructors. To refresh it, each object stores a pointer (vptr) to each of its vtables (in the event of multiple inheritance there can be more than one), when the compiler sees a call to any virtual function, it follows the vptr of the static type of the pointer to find the vtable and then uses the pointer in that table to forward the call. In most cases the call can be directly dispatched, in others (multiple inheritance) it calls some intermediate code (thunk) that fixes the this pointer to refer to the type of the final overrider for that function.
How does the compiler then call the base destructors?
The process of destructing an object takes more operations than those you write inside the body of the destructor. When the compiler generates the code for the destructor, it adds extra code both before and after the user defined code.
Before the first line of a user defined destructor is called, the compiler injects code that will make the type of the object be that of the destructor being called. That is, right before ~derived is entered, the compiler adds code that will modify the vptr to refer to the vtable of derived, so that effectively, the runtime type of the object becomes derived (*).
After the last line of your user defined code, the compiler injects calls to the member destructors as well as base destructor(s). This is performed disabling dynamic dispatch, which means that it will no longer come all the way down to the just executed destructor. It is the equivalent of adding this->~mybase(); for each base of the object (in reverse order of declaration of the bases) at the end of the destructor.
With virtual inheritance, things get a bit more complex, but overall they follow this pattern.
EDIT (forgot the (*)):
(*) The standard mandates in §12/3:
When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the con- structor or destructor’s class, or overriding it in one of the other base classes of the most derived object.
That requirement implies that the runtime type of the object is that of the class being constructed/destructed at this time, even if the original object that is being constructed/destructed is of a derived type. A simple test to verify this implementation can be:
struct base {
virtual ~base() { f(); }
virtual void f() { std::cout << "base"; }
};
struct derived : base {
void f() { std::cout << "derived"; }
};
int main() {
base * p = new derived;
delete p;
}
A virtual destructor is treated in the same way as any other virtual function. I note that you've correctly maked the base class's destructor as virtual. As such, it is no way different than any other virtual function, as far as dynamic dispatch is concerned. The most derived class destructor gets called through dynamic dispatch but it also automatically results in calls to Base class destructors of the class1.
Most compiler implements this feature using vtable and vptr, though the language specification does not mandate it. There can be a compiler which does this differently, without using vtable and vptr.
Anyway, as it true for most compilers, it is worth knowing what vtable is. So vtable is a table contains pointers of all virtual functions the class defines, and the compiler adds vptr to the class as hidden pointer which points to the correct vtable, so the compiler uses correct index, calculated at compile-time, to the vtable so as to dispatch the correct virtual function at runtime.
1. The italicized text is taken from #Als's comment. Thanks to him. It makes things more clear.
A suitable implementation of (virtual) destructors the compiler might use would be (in pseudocode)
class Base {
...
virtual void __destruct(bool should_delete);
...
};
void Base::__destruct(bool should_delete)
{
this->__vptr = &Base::vtable; // Base is now the most derived subobject
... your destructor code ...
members::__destruct(false); // if any, in the reverse order of declaration
base_classes::__destruct(false); // if any
if(should_delete)
operator delete(this); // this would call operator delete defined here, or inherited
}
This function gets defined even if you didn't define a destructor. Your code would just be empty in that case.
Now all derived classes would override (automatically) this virtual function:
class Der : public Base {
...
virtual void __destruct(bool should_delete);
...
};
void Der::__destruct(bool should_delete)
{
this->__vptr = &Der::vtable;
... your destructor code ...
members::__destruct(false);
Base::__destruct(false);
if(should_delete)
operator delete(this);
}
A call delete x, where x is of pointer to class type, would be translated as
x->__destruct(true);
and any other destructor call (implicit due to variable going out of scope, explicit x.~T()) would be
x.__destruct(false);
This results in
the most derived destructor always being called (for virtual destructors)
operator delete from the most derived object being called
all members' and base classes' destructors being called.
HTH. This should be understandable if you understand virtual functions.
As usual with virtual functions there will be some implementation mechanism (like a vtable pointer) that will let the compiler find which destructor to run first depending on the type of the object. Once the most derived class destructor is run it will in turn run the base class destructor and so on.
It's up to the compiler how to implement it and typically it's done with the same mechanism as other virtual methods. In other words there's nothing special about destructors that requires a virtual method dispatch mechanism that is distinct from that used by normal methods.
A virtual destructor has an entry in the virtual table just as other virtual functions do. When the destructor is invoked -either manually or automatically from a call to delete- the most derived version is invoked. A destructor also automatically calls the destructor for its base classes, so that in combination with the virtual dispatch is what causes the magic.
Unlike other virtual functions, when you override a virtual destructor, your object's virtual destructor is called in addition to any inherited virtual destructors.
Technically this can be achieved by whatever means the compiler chooses, but almost all compilers achieve this via static memory called a vtable, which permits polymorphism on functions and destructors. For each class in your source code, a static constant vtable is generated for it at compile time. When an object of type T is constructed at runtime, the object's memory is initialized with a hidden vtable pointer which points to the T's vtable in ROM. Inside the vtable is a list of member function pointers and a list of destructor function pointers. When a variable of any type that has a vtable goes out of scope or is deleted with delete or delete[], all of the destructor pointers in vtable the object points to are all invoked. (Some compilers choose to only store the most derived destructor pointer in the table, and then include a hidden invocation of the superclass's destructor in the body of every virtual destructor if one exists. This results in equivalent behavior.)
Additional magic is needed for virtual and nonvirtual multiple inheritance. Assume I am deleting a pointer p, where p is of the type of a base-class. We need to invoke the destructor of the sub-classes with this=p. But using multiple inheritance, p and the start of the derived object may not be the same! There is a fixed offset which must be applied. There is one such offset stored in the vtable for each class that is inherited, as well as a set of inherited offsets.
When you have a pointer to an object, it points to a block of memory that has both the data for that object and a 'vtable pointer.' In microsoft compilers, the vtable pointer is the first piece of data in the object. In Borland compilers, it is the last. Either way, it points to a vtable that represents a list of function vectors corresponding to the virtual methods that can be invoked for that object/class. The virtual destructor is just another vector in that list of function pointer vectors.