Say we have the classes.
class A
{
public:
void doSomething() = 0;
A();
virtual ~A();
private:
vector<SomeStuff> cont;
bool yesNo;
}
class B: public A
{
public:
B();
~B();
private:
bool Byesno;
}
So the constructor of A gets called first and then the constructor of B gets called after that, and when we destroy B, the destructor of B gets called first and the destructor of A gets called after that. So basically, the destructor of A will delete the inherited variables of B and and the destructor of B will delete its class-specific ones. Am I right?
what I don't understand is how can we call the destructor of A if we can't even instantiate an object of type A? How does it work internally?
B is A and some more. Destructor of A will be called to ensure that A part of B is cleaned up. Your classes don't use the virtual keyword, so I'm not sure why you are wondering about virtual destructors, but since you are wondering this might help:
class A
{
public:
virtual ~A() { cout << "A::~A()" << endl; }
};
class B : public A
{
public:
~B() { cout << "B::~B()" << endl; }
};
int main()
{
A* obj = new B();
delete obj;
return 0;
}
The output as you'd expect will be
B::~B()
A::~A()
But, if you didn't declare A's destructor virtual, the output will simply be
A::~A()
So, in conclusion, if your code involves polymorphism and you want the destructor of the pointed to object to be called as opposed to the destructor of pointer's type itself, you will have to declare your base-class destructor virtual.
Each destructor runs through three stages:
user code
delegate destruction of class members to the respective destructors
delegate destruction of base classes to the respective destructors
The destructors for the primitive types are normally trivial, i.e. do nothing. Technically, you could think of
B::~B() { Byesno = false; }
as
B::~B()
{
Byesno = false; // explicit
Byesno.~bool(); // implicit, member
A::~A(); // implicit, base class
}
A::~A()
{
yesNo.~bool(); // implicit, member
cont.~vector(); // implicit, member
}
~bool() is empty and will be inlined, so the only call you can see is the one to ~vector().
All this is a separate matter from which d'tor is called. If A's d'tor is non-virtual, B's d'tor will be called only if the object being destructed is known to be a B object:
A a;
B b;
A *ap = new B;
delete ap;
This will use ~A() for the a object (correct), ~B() for the b object (also correct), and ~A() for the object pointed to by ap (incorrect, but we don't know better). If ~A() were virtual, the d'tor call itself would be looked up in the v'table, which contains a pointer to ~B() then.
It is a tiny bit more complex than that, but not much.
Related
I am a newbie and I know this is a very basic concept and might be a duplicate too.
Is it not true that once a constructor is called its corresponding destructor has to be called?
[code run on Dev C++]
class Base
{
public:
Base() { cout<<"Base Constructor\n";}
int b;
~Base() {cout << "Base Destructor\n"; }
};
class Derived:public Base
{
public:
Derived() { cout<<"Derived Constructor\n";}
int a;
~Derived() { cout<< "Derived Destructor\n"; }
};
int main () {
Base* b = new Derived;
//Derived *b = new Derived;
delete b;
getch();
return 0;
}
GIVES OUTPUT
Base Constructor
Derived Constructor
Base Destructor
Your code has undefined behavior. The base class's destructor must be virtual for the following to have defined behavior.
Base* b = new Derived;
delete b;
From the C++ standard:
5.3.5 Delete
3 In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
So in your case, the static type is Base, and the dynamic type is Derived. So the Base's destructor should be:
virtual ~Base() {cout << "Base Destructor\n"; }
There is no need to call a destructor if it is trivial.
That does not help in your example though, because if a class has a sub-object with non-trivial destructor (member or base) or its own destructor is user-defined, it is not trivial.
Also, you may only delete a class using a pointer to base, if that base-class has a virtual destructor, on pain of undefined behavior (The compiler need not warn you).
Pro-tip: If you need to make it virtual (for example due to above requirement), but do not want to prevent it from being trivial (some containers and algorithms have optimized implementations for trivial types), use:
virtual ~MyClass = default; // Since C++11
So, never delete using a pointer to Base if Base does not have a virtual destructor or it is actually really a base.
Example:
class Base {
public:
virtual void f() = 0;
virtual ~Base() { std::cout << "Base::~Base()\n"; }
};
class Derived : public Base {
public:
void f() { }
~Derived() { std::cout << "Derived::~Derived()\n"; }
};
int main() {
Base* p = new Derived();
delete p;
return 0;
}
Output:
Derived::~Derived()
Base::~Base()
I thought only the derived class destructor will be called since the pointed object to be freed is an instance of the derived class.
I have two questions:
Why was the virtual base destructor called?
Is it legally possible (or should it even be possible) to prevent the base class destructor from being called?
Why was the virtual base destructor called?
Because base class should be properly destructed. Derived class can not possibly do this.
Is it legally possible (or should it even be possible) to prevent the
base class destructor from being called?
No, because it would prevent properly destructing the base class (i.e. call it's members destructors)
I am a newbie and I know this is a very basic concept and might be a duplicate too.
Is it not true that once a constructor is called its corresponding destructor has to be called?
[code run on Dev C++]
class Base
{
public:
Base() { cout<<"Base Constructor\n";}
int b;
~Base() {cout << "Base Destructor\n"; }
};
class Derived:public Base
{
public:
Derived() { cout<<"Derived Constructor\n";}
int a;
~Derived() { cout<< "Derived Destructor\n"; }
};
int main () {
Base* b = new Derived;
//Derived *b = new Derived;
delete b;
getch();
return 0;
}
GIVES OUTPUT
Base Constructor
Derived Constructor
Base Destructor
Your code has undefined behavior. The base class's destructor must be virtual for the following to have defined behavior.
Base* b = new Derived;
delete b;
From the C++ standard:
5.3.5 Delete
3 In the first alternative (delete object), if the static type of the
operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
So in your case, the static type is Base, and the dynamic type is Derived. So the Base's destructor should be:
virtual ~Base() {cout << "Base Destructor\n"; }
There is no need to call a destructor if it is trivial.
That does not help in your example though, because if a class has a sub-object with non-trivial destructor (member or base) or its own destructor is user-defined, it is not trivial.
Also, you may only delete a class using a pointer to base, if that base-class has a virtual destructor, on pain of undefined behavior (The compiler need not warn you).
Pro-tip: If you need to make it virtual (for example due to above requirement), but do not want to prevent it from being trivial (some containers and algorithms have optimized implementations for trivial types), use:
virtual ~MyClass = default; // Since C++11
So, never delete using a pointer to Base if Base does not have a virtual destructor or it is actually really a base.
I am trying to understand virtual destructors. The following is a copy paste from this page When to use virtual destructors?
Here, you'll notice that I didn't declare Base's destructor to be
virtual. Now, let's have a look at the following snippet:
Base *b = new Derived(); // use b
delete b; // Here's the problem!
[...] If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and non-virtual; by doing so, the compiler won't let you call delete on a base class pointer.
I don't understand why the deletion is prevented by having a protected non-virtual base class destructor. Doesn't the compiler think that we're trying to call delete from a base class object? What does protected have to do with that?
The C++ Standard has this to say about delete (section 5.3.5p10):
Access and ambiguity control are done for both the deallocation function and the destructor (12.4, 12.5).
Therefore, only code that has access to the destructor is able to use delete. Since the destructor is protected, that means that no one can call delete on a pointer of type Base*. Only subclasses can use the destructor at all (and the only thing that will is the subclass's own destructor, as part of the subobject destruction process).
Of course, the subclass should make its own destructor public, allowing you to delete objects through the subclass type (assuming that is the correct actual type).
NOTE: Actually, other members of Base can do delete (Base*)p; since they have access. But C++ assumes that someone using this construct will not be doing that -- C++ access control only provides guidance to code outside your class.
delete b; effectively performs b->~Base(); deallocate(b);. The first part - calling the destructor - would fail to compile if the destructor is inaccessible (in the same way that calling any other inaccessible method fails).
From my understanding (based on this page), the only case we would like to use the non-virtual and protected destructor in the base class is the following:
#include <iostream>
struct unary_function {
protected:
~unary_function() {
std::cout << "unary_function" << std::endl;
}
};
struct IsOdd : public unary_function {
public:
bool operator()(int number) {
return (number % 2 != 0);
}
};
void f(unary_function *f) {
// compile error
// delete f;
}
int main() {
// unary_function *a = new IsOdd;
// delete a;
IsOdd *a = new IsOdd;
delete a;
getchar();
return 0;
}
therefore, you can only do this:
IsOdd *a = new IsOdd;
delete a;
or
IsOdd c;
never these:
unary_function *a = new IsOdd;
delete a;
therefore, with nonvirtual protected destructor, the compiler would give an error when you try to use this
void f(unary_function *f) {
delete f;
// this function couldn't get compiled because of this delete.
// you would have to use the derived class as the parameter
}
Protected methods and variables of a class (let's call it Base) can only be accessed by derived classes. Thus, if you call delete on a pointer of type Base outside a derived class, it will try to call Base::~Base() (Base's destructor), but since it is protected, it cannot be called, which thus results in a compilation error.
According to the specification, the destructor of a base class must only be declared protected and non-virtual (to not allow deletion of a derived object through a Base pointer), or public and virtual (to allow safe deletion of a derived object through a Base pointer).
If a destructor is declared public and non-virtual, it results in undefined behaviour if a pointer of type Base which points to a derived class is deleted.
The two options:
Non-virtual protected destructor - Base pointer to Derived cannot be deleted:
class Base {
public:
Base() {
std::cout << "Base ctor called.\n";
}
protected:
~Base() {
std::cout << "Base dtor called.\n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived ctor called.\n";
}
~Derived() {
std::cout << "Derived dtor called.\n";
}
};
Base *foo = new Derived;
delete foo; // compilation error
... as mentioned in your question.
Public virtual destructor - allows a pointer of type Base to a Derived object to be deleted. First the Derived destructor is called, and then the Base destructor is called:
class Base {
public:
Base() {
std::cout << "Base ctor called.\n";
}
virtual ~Base() {
std::cout << "Base dtor called.\n";
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived ctor called.\n";
}
~Derived() override {
std::cout << "Derived dtor called.\n";
}
};
Base *foo = new Derived;
delete foo;
Output:
Base ctor called.
Derived ctor called.
Derived dtor called.
Base dtor called.
Why are all destructors, ~D(),~C(),~B(),~A() being called in the example below?
There is only one virtual destructor: that of A.
Here is the code:
#include<iostream>
using namespace std;
class A
{
public:
virtual ~A()
{
cout<<"destruct A\n";
}
};
class B:public A
{
public:
~B()
{
cout<<"destruct B\n";
}
};
class C:public B
{
public:
~C()
{
cout<<"destruct C\n";
}
};
class D:public C
{
public:
~D()
{
cout<<"destruct D\n";
}
};
int main()
{
A* ptr = new D();
delete ptr;
return 0;
}
Once A's destructor is declared virtual, the destructors of all derived classes are also virtual, even if they aren't explicitly declared as such.. So the behaviour you see is exactly what is expected
The destruction order in derived objects goes in exactly the reverse
order of construction: first the destructors of the most derived
classes are called and then the destructor of the base classes.
A destructor can be defined as virtual or even pure virtual. You would
use a virtual destructor if you ever expect a derived class to be
destroyed through a pointer to the base class. This will ensure that
the destructor of the most derived classes will get called:
A* b1 = new B;//if A has a virtual destructor
delete b1;//invokes B's destructor and then A's
A* b1 = new B;//if A has no virtual destructor
delete b1;//invokes A's destructor ONLY
If A does not have a virtual destructor, deleting b1 through a pointer
of type A will merely invoke A's destructor. To enforce the calling of
B's destructor in this case we must have specified A's destructor as
virtual:
virtual ~A();
REFERENCE
As #juanchopanza said - declaring the base destructor virtual means all descendants have virtual destructors. This inherited virtuality is the same for any methods, not just destructors.
It's why I have interviewed people who didn't know what the keyword did because they had only ever had to override methods derived from a framework, so they were all virtual (sigh).