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.
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)
When I am trying to delete the derived object polymorphically (that is: base class has public virtual destructor) why derived class private destructor is still being called? Why the scope resolution private is not working here.
class Base
{
protected:
Base() { cout << "Base constructor.\n"; }
public:
virtual ~Base() { cout << "Base destructor.\n"; }
};
class Derived :public Base
{
public:
Derived() { cout << "Derived constructor.\n"; }
private:
~Derived() { cout << "Derived destructor.\n"; }
};
int main()
{
Base *p = new Derived();
delete p;
}
Output:
Base constructor.
Derived constructor.
Derived destructor.
Base destructor.
Because destructors are called in reversed order of constructors and virtual destructor will always be called.
private has nothing to do if a virtual function is going to be called.
As I pointed here out:
Why would a virtual function be private?
ISO C++ 1998 Standard onwards explicitly states:
§10.3 [...] Access control (clause 11) is not considered in determining overriding.
A bit philosophical offtopic:
Going further this is what STL does for iostreams: Definition of Non-Virtual Interface, i.e. all public functions (with exception of destructors) are non-virtual and all virtual functions are either protected or private. Public functions call virtual protected or private ones. This gives a very clear entry point into the entire hierarchy.
It is, but you're not calling ~Derived() directly. If you were to use
Derived *p = new Derived();
delete p;
then you'd get the error. But when you access ~Derived() indirectly via polymorphism (e.g. by calling ~Base()), then the access specifier private does not apply.
According to [class.access.virt#1]:
The access rules (Clause [class.access]) for a virtual function are determined by its declaration and are not affected by the rules for a function that later overrides it. [ Example:
class B {
public:
virtual int f();
};
class D : public B {
private:
int f();
};
void f() {
D d;
B* pb = &d;
D* pd = &d;
pb->f(); // OK: B::f() is public, D::f() is invoked
pd->f(); // error: D::f() is private
}
— end example ]
And again in footnote 111 in [class.virtual]:
Access control is not considered in determining overriding.
You use a virtual destructor because you want all the destructor in your inheritance to get called. That exactly what you get. The private does not apply as you don't call the destructor explicitly.
If your destructor was not virtual you will get only Base::~Base() called. Usually that's not what you want when you have polymorphism.
You can call the destructor through a pointer to B, because it's already public there. It's the static type of the pointer which is used to determine that access in this case.
[class.access.virt/1]
The access rules (Clause [class.access]) for a virtual function are
determined by its declaration and are not affected by the rules for a
function that later overrides it. [ Example:
class B {
public:
virtual int f();
};
class D : public B {
private:
int f();
};
void f() {
D d;
B* pb = &d;
D* pd = &d;
pb->f(); // OK: B::f() is public, D::f() is invoked
pd->f(); // error: D::f() is private
}
— end example ]
This keeps virtual functions in line with the Liskov Substitution Principle
You delete the derived class through the base class pointer. With the help of the virtual destructor you start the delete at the derived class. Because that class can access its private members, it call the private destructor. After that the base class is calling the public destructor.
Note that you call delete and not calling de destructor Derived::~Derived() direct!
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.
During destruction of the derived class object, i first hit the derived class destructor and then the base class destructor (which is as expected). But i was curious to find out - at what point does the functions of the derived class go out of scope (are destroyed).
Does it happen as soon as the control leaves the derived class destructor and goes toward the base? Or does it happen once we done with the base class destructor also.
Thanks
Once the destructor of the most derived class finishes, the dynamic type of the object can be considered that of the next less-derived-type. That is, a call to a virtual method in the base destructor will find that the final overrider at that point in time is at base level. (The opposite occurs during construction)
struct base {
base() { std::cout << type() << std::endl; }
virtual ~base() { std::cout << type() << std::endl; }
virtual std::string type() const {
return "base";
}
};
struct derived : base {
virtual std::string type() const {
return "derived";
}
};
int main() {
base *p = new derived;
std::cout << p->type() << std::endl;
delete p;
}
// output:
// base
// derived
// base
Functions don't get destroyed.
Virtual functions however get their entry in the v-table erased as soon as the derived destructor finishes so you can't call derived virtual functions from the base d'tor.