I have a solid understanding of most OOP
theory but the one thing that confuses me a lot is virtual destructors.
I thought that the destructor always gets called no matter what and for every object in the chain.
When are you meant to make them virtual and why?
virtual
makes sure it starts at the top instead of the middle. - anyone Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
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!
Since Base's destructor is not virtual
and b
is a Base*
pointing to a Derived
object, delete b
has undefined behaviour:
[In
delete b
], 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 most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.
To sum up, always make base classes' destructors virtual
when they're meant to be manipulated polymorphically.
If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete
on a base class pointer.
You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.
Answered 2023-09-21 08:09:30
Base
and Derived
have all automatic storage variables? ie there is no "special" or additional custom code to execute in the destructor. Is it ok then to leave off writing any destructors at all? Or will the derived class still have a memory leak? - anyone A virtual constructor is not possible but virtual destructor is possible. Let us experiment.......
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
~Base(){
cout << "Base Destructor called\n";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor called\n";
}
~Derived1(){
cout << "Derived destructor called\n";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
The above code output the following:
Base Constructor Called
Derived constructor called
Base Destructor called
The construction of derived object follow the construction rule but when we delete the "b" pointer(base pointer) we have found that only the base destructor is called. But this must not happen. To do the appropriate thing, we have to make the base destructor virtual. Now let see what happens in the following:
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
virtual ~Base(){
cout << "Base Destructor called\n";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor called\n";
}
~Derived1(){
cout << "Derived destructor called\n";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
The output changed as following:
Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called
So the destruction of the base pointer (which takes an allocation on derived object!) follows the destruction rule, i.e first the Derived, then the Base. On the other hand, there is nothing like a virtual constructor.
Answered 2023-09-21 08:09:30
Declare destructors virtual in polymorphic base classes. This is Item 7 in Scott Meyers' Effective C++. Meyers goes on to summarize that if a class has any virtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.
Answered 2023-09-21 08:09:30
const Base& = make_Derived();
. In this case, the destructor of the Derived
prvalue will be called, even if it's not virtual, so one saves the overhead introduced by vtables/vpointers. Of course the scope is quite limited. Andrei Alexandrescu mentioned this in his book Modern C++ Design. - anyone Also be aware that deleting a base class pointer when there is no virtual destructor will result in undefined behavior. Something that I learned just recently:
How should overriding delete in C++ behave?
I've been using C++ for years and I still manage to hang myself.
Answered 2023-09-21 08:09:30
struct Base {
virtual void f() {}
virtual ~Base() {}
};
struct Derived : Base {
void f() override {}
~Derived() override {}
};
Base* base = new Derived;
base->f(); // calls Derived::f
base->~Base(); // calls Derived::~Derived
Virtual destructor call is no different from any other virtual function call.
For base->f()
, the call will be dispatched to Derived::f()
, and it's the same for base->~Base()
- its overriding function - the Derived::~Derived()
will be called.
Same happens when destructor is being called indirectly, e.g. delete base;
. The delete
statement will call base->~Base()
which will be dispatched to Derived::~Derived()
.
If you are not going to delete object through a pointer to its base class - then there is no need to have a virtual destructor. Just make it protected
so that it won't be called accidentally:
// library.hpp
struct Base {
virtual void f() = 0;
protected:
~Base() = default;
};
void CallsF(Base& base);
// CallsF is not going to own "base" (i.e. call "delete &base;").
// It will only call Base::f() so it doesn't need to access Base::~Base.
//-------------------
// application.cpp
struct Derived : Base {
void f() override { ... }
};
int main() {
Derived derived;
CallsF(derived);
// No need for virtual destructor here as well.
}
Answered 2023-09-21 08:09:30
~Derived()
in all derived classes, even if it's just ~Derived() = default
? Or is that implied by the language (making it safe to omit)? - anyone protected
section, or to ensure that it's virtual by using override
. - anyone base->~Base()
? As per what you said, the Base::~Base()
would not be invoked, then there would be memory leaking. Am I right? - anyone Virtual keyword for destructor is necessary when you want different destructors should follow proper order while objects is being deleted through base class pointer. for example:
Base *myObj = new Derived();
// Some code which is using myObj object
myObj->fun();
//Now delete the object
delete myObj ;
If your base class destructor is virtual then objects will be destructed in a order(firstly derived object then base ). If your base class destructor is NOT virtual then only base class object will get deleted(because pointer is of base class "Base *myObj"). So there will be memory leak for derived object.
Answered 2023-09-21 08:09:30
To be simple, Virtual destructor is to destruct the resources in a proper order, when you delete a base class pointer pointing to derived class object.
#include<iostream>
using namespace std;
class B{
public:
B(){
cout<<"B()\n";
}
virtual ~B(){
cout<<"~B()\n";
}
};
class D: public B{
public:
D(){
cout<<"D()\n";
}
~D(){
cout<<"~D()\n";
}
};
int main(){
B *b = new D();
delete b;
return 0;
}
OUTPUT:
B()
D()
~D()
~B()
==============
If you don't give ~B() as virtual. then output would be
B()
D()
~B()
where destruction of ~D() is not done which leads to leak
Answered 2023-09-21 08:09:30
delete
on a base pointer leads to undefined behavior. - anyone I like to think about interfaces and implementations of interfaces. In C++ speak interface is pure virtual class. Destructor is part of the interface and expected to implemented. Therefore destructor should be pure virtual. How about constructor? Constructor is actually not part of the interface because object is always instantiated explicitly.
Answered 2023-09-21 08:09:30
virtual
in a base class, it is automatically virtual
in a derived class, even if it is not declared so. - anyone Virtual base class destructors are "best practice" - you should always use them to avoid (hard to detect) memory leaks. Using them, you can be sure all destructors in the inheritance chain of your classes are beeing called (in proper order). Inheriting from a base class using virtual destructor makes the destructor of the inheriting class automatically virtual, too (so you do not have to retype 'virtual' in the inheriting class destructor declaration).
Answered 2023-09-21 08:09:30
const
global variable versus a non-const
one? Even if you do, I guarantee most don't with many not even knowing the two types of linkage exist. - anyone I think the core of this question is about virtual methods and polymorphism, not the destructor specifically. Here is a clearer example:
class A
{
public:
A() {}
virtual void foo()
{
cout << "This is A." << endl;
}
};
class B : public A
{
public:
B() {}
void foo()
{
cout << "This is B." << endl;
}
};
int main(int argc, char* argv[])
{
A *a = new B();
a->foo();
if(a != NULL)
delete a;
return 0;
}
Will print out:
This is B.
Without virtual
it will print out:
This is A.
And now you should understand when to use virtual destructors.
Answered 2023-09-21 08:09:30
B b{}; A& a{b}; a.foo();
. Checking for NULL
- which should be nullptr
- before delete
ing - with incorrect indendation - is not required: delete nullptr;
is defined as a no-op. If anything, you should have checked this before calling ->foo()
, as otherwise undefined behaviour can occur if the new
somehow failed.) - anyone delete
on a NULL
pointer (i.e., you don't need the if (a != NULL)
guard). - anyone std::vector<Base*>
. Of course, std::vector<Base&>
isn't a thing. - anyone If you use shared_ptr
(only shared_ptr, not unique_ptr), you don't have to have the base class destructor virtual:
#include <iostream>
#include <memory>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
~Base(){ // not virtual
cout << "Base Destructor called\n";
}
};
class Derived: public Base
{
public:
Derived(){
cout << "Derived constructor called\n";
}
~Derived(){
cout << "Derived destructor called\n";
}
};
int main()
{
shared_ptr<Base> b(new Derived());
}
output:
Base Constructor Called
Derived constructor called
Derived destructor called
Base Destructor called
Answered 2023-09-21 08:09:30
virtual
keyword could save you from a lot of agony. - anyone unique_ptr
is changed after c++14 e.g add make_unique
. Maybe the committee imporved
the unique_ptr
? - anyone What is a virtual destructor or how to use virtual destructor
A class destructor is a function with same name of the class preceding with ~ that will reallocate the memory that is allocated by the class. Why we need a virtual destructor
See the following sample with some virtual functions
The sample also tell how you can convert a letter to upper or lower
#include "stdafx.h"
#include<iostream>
using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
//void convertch(){};
virtual char* convertChar() = 0;
~convertch(){};
};
class MakeLower :public convertch
{
public:
MakeLower(char *passLetter)
{
tolower = true;
Letter = new char[30];
strcpy(Letter, passLetter);
}
virtual ~MakeLower()
{
cout<< "called ~MakeLower()"<<"\n";
delete[] Letter;
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
Letter[i] = Letter[i] + 32;
return Letter;
}
private:
char *Letter;
bool tolower;
};
class MakeUpper : public convertch
{
public:
MakeUpper(char *passLetter)
{
Letter = new char[30];
toupper = true;
strcpy(Letter, passLetter);
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
Letter[i] = Letter[i] - 32;
return Letter;
}
virtual ~MakeUpper()
{
cout<< "called ~MakeUpper()"<<"\n";
delete Letter;
}
private:
char *Letter;
bool toupper;
};
int _tmain(int argc, _TCHAR* argv[])
{
convertch *makeupper = new MakeUpper("hai");
cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" ";
delete makeupper;
convertch *makelower = new MakeLower("HAI");;
cout<<"Eneterd : HAI = " <<makelower->convertChar()<<" ";
delete makelower;
return 0;
}
From the above sample you can see that the destructor for both MakeUpper and MakeLower class is not called.
See the next sample with the virtual destructor
#include "stdafx.h"
#include<iostream>
using namespace std;
// program to convert the lower to upper orlower
class convertch
{
public:
//void convertch(){};
virtual char* convertChar() = 0;
virtual ~convertch(){}; // defined the virtual destructor
};
class MakeLower :public convertch
{
public:
MakeLower(char *passLetter)
{
tolower = true;
Letter = new char[30];
strcpy(Letter, passLetter);
}
virtual ~MakeLower()
{
cout<< "called ~MakeLower()"<<"\n";
delete[] Letter;
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] + 32;
}
return Letter;
}
private:
char *Letter;
bool tolower;
};
class MakeUpper : public convertch
{
public:
MakeUpper(char *passLetter)
{
Letter = new char[30];
toupper = true;
strcpy(Letter, passLetter);
}
char* convertChar()
{
size_t len = strlen(Letter);
for(int i= 0;i<len;i++)
{
Letter[i] = Letter[i] - 32;
}
return Letter;
}
virtual ~MakeUpper()
{
cout<< "called ~MakeUpper()"<<"\n";
delete Letter;
}
private:
char *Letter;
bool toupper;
};
int _tmain(int argc, _TCHAR* argv[])
{
convertch *makeupper = new MakeUpper("hai");
cout<< "Eneterd : hai = " <<makeupper->convertChar()<<" \n";
delete makeupper;
convertch *makelower = new MakeLower("HAI");;
cout<<"Eneterd : HAI = " <<makelower->convertChar()<<"\n ";
delete makelower;
return 0;
}
The virtual destructor will call explicitly the most derived run time destructor of class so that it will be able to clear the object in a proper way.
Or visit the link
Answered 2023-09-21 08:09:30
I thought it would be beneficial to discuss the "undefined" behavior, or at least the "crash" undefined behavior that may occur when deleting through a base class(/struct) without a virtual destructor, or more precisely no vtable. The code below list a few simple structs (the same would be true for classes).
#include <iostream>
using namespace std;
struct a
{
~a() {}
unsigned long long i;
};
struct b : a
{
~b() {}
unsigned long long j;
};
struct c : b
{
~c() {}
virtual void m3() {}
unsigned long long k;
};
struct d : c
{
~d() {}
virtual void m4() {}
unsigned long long l;
};
int main()
{
cout << "sizeof(a): " << sizeof(a) << endl;
cout << "sizeof(b): " << sizeof(b) << endl;
cout << "sizeof(c): " << sizeof(c) << endl;
cout << "sizeof(d): " << sizeof(d) << endl;
// No issue.
a* a1 = new a();
cout << "a1: " << a1 << endl;
delete a1;
// No issue.
b* b1 = new b();
cout << "b1: " << b1 << endl;
cout << "(a*) b1: " << (a*) b1 << endl;
delete b1;
// No issue.
c* c1 = new c();
cout << "c1: " << c1 << endl;
cout << "(b*) c1: " << (b*) c1 << endl;
cout << "(a*) c1: " << (a*) c1 << endl;
delete c1;
// No issue.
d* d1 = new d();
cout << "d1: " << d1 << endl;
cout << "(c*) d1: " << (c*) d1 << endl;
cout << "(b*) d1: " << (b*) d1 << endl;
cout << "(a*) d1: " << (a*) d1 << endl;
delete d1;
// Doesn't crash, but may not produce the results you want.
c1 = (c*) new d();
delete c1;
// Crashes due to passing an invalid address to the method which
// frees the memory.
d1 = new d();
b1 = (b*) d1;
cout << "d1: " << d1 << endl;
cout << "b1: " << b1 << endl;
delete b1;
/*
// This is similar to what's happening above in the "crash" case.
char* buf = new char[32];
cout << "buf: " << (void*) buf << endl;
buf += 8;
cout << "buf after adding 8: " << (void*) buf << endl;
delete buf;
*/
}
I'm not suggesting whether you need virtual destructors or not, though I think in general it's a good practice to have them. I'm just pointing out the reason you may end up with a crash if your base class(/struct) does not have a vtable and your derived class(/struct) does and you delete an object via a base class(/struct) pointer. In this case, the address you pass to the heap's free routine is invalid and thus the reason for the crash.
If you run the above code you'll see clearly when the issue occurs. When the this pointer of the base class(/struct) is different from the this pointer of the derived class(/struct) you're going to run into this problem. In the sample above, struct a and b don't have vtables. structs c and d do have vtables. Thus an a or b pointer to a c or d object instance will be fixed up to account for the vtable. If you pass this a or b pointer to delete it will crash due to the address being invalid to the heap's free routine.
If you plan to delete derived instances which have vtables from base class pointers, you need to ensure the base class has a vtable. One way to do that is to add a virtual destructor, which you might want anyway to properly clean up resources.
Answered 2023-09-21 08:09:30
I propose this: If a class or struct is not final
, you should define virtual destructor for it.
I know this looks like an excessively vigilant overkill to become a rule of thumb. But, it is the only way to be sure that someone deriving from your class won't have UB when deleting with base pointer.
Scott Meyers' recommendation in Effective C++ quoted below is good but not enough to be sure.
if a class has any virtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.
For example, in the program below, base class B does not have any virtual functions, so according to Meyer, you don't need to write a virtual destructor. However, you have UB below, if you don't:
#include <iostream>
struct A
{
~A()
{
std::cout << "A::~A()" << std::endl;
}
};
struct B
{
};
struct C : public B
{
A a;
};
int main(int argc, char *argv[])
{
B *b = new C;
delete b; // UB, and won't print "A::~A()"
return 0;
}
Answered 2023-09-21 08:09:30
when you need to call derived class destructor from base class. you need to declare virtual base class destructor in base class.
Answered 2023-09-21 08:09:30
Make all destructors virtual unless you have good reason not to.
Otherwise evil like this happens:
Suppose you have an array of Fruit pointers with both Apple and Orange objects.
When you delete from the collection of Fruit objects, ~Apple() and ~Orange() fail to be called unless ~Fruit() is virtual.
Example done right:
#include <iostream>
using namespace std;
struct Fruit { // good
virtual ~Fruit() { cout << "peel or core should have been tossed" << endl; }
};
struct Apple: Fruit { virtual ~Apple() {cout << "toss core" << endl; } };
struct Orange: Fruit { virtual ~Orange() {cout << "toss peel" << endl; } };
int main() {
Fruit *basket[]={ new Apple(), new Orange() };
for (auto fruit: basket) delete fruit;
};
good output
toss core
peel or core should have been tossed
toss peel
peel or core should have been tossed
Example done wrong:
#include <iostream>
using namespace std;
struct Fruit { // bad
~Fruit() { cout << "peel or core should have been tossed" << endl; }
};
struct Apple: Fruit { virtual ~Apple() {cout << "toss core" << endl; } };
struct Orange: Fruit { virtual ~Orange() {cout << "toss peel" << endl; } };
int main() {
Fruit *basket[]={ new Apple(), new Orange() };
for (auto fruit: basket) delete fruit;
};
bad output
peel or core should have been tossed
peel or core should have been tossed
(Note: Where I used struct for brevity, normally use class and specify public)
Answered 2023-09-21 08:09:30
I think most answers here miss the point, with the exception of the accepted one, which is a good thing. However, let me add one more with a different view on the issue: You need a virtual destructor if you want to polymorphically delete instances of this class.
This kind-of sidesteps the question, so let me elaborate: As many pointed out, you get undesired behaviour if you invoke delete base_ptr
and the destructor is not virtual. However, there are several assumptions there that need to be make explicit:
std::string
. C++ allows you to shoot yourself in the foot. This is your fault though, not that of the baseclass not having a virtual destructor.For a similarly different view on the subject, also read When should you not use virtual destructors?
Answered 2023-09-21 08:09:30
A basic definition about virtual
is it determines if a member function of a class can be over-ridden in its derived classes.
A class's D-tor is called basically at the end of the scope, but there is a problem, for example when we define an instance on the Heap (dynamic allocation), we should delete it manually.
As soon as the instruction get executed, the base class destructor get called, but not for the derived one.
A Pratical example is when, in control field, you have to manipulate effectors, actuators.
At the end of the scope, if the destructor of one of the power elements (Actuator), isn't called, there will be fatal consequences.
#include <iostream>
class Mother{
public:
Mother(){
std::cout<<"Mother Ctor"<<std::endl;
}
virtual~Mother(){
std::cout<<"Mother D-tor"<<std::endl;
}
};
class Child: public Mother{
public:
Child(){
std::cout<<"Child C-tor"<<std::endl;
}
~Child(){
std::cout<<"Child D-tor"<<std::endl;
}
};
int main()
{
Mother *c = new Child();
delete c;
return 0;
}
Answered 2023-09-21 08:09:30
Any class that is inherited publicly, polymorphic or not, should have a virtual destructor. To put another way, if it can be pointed to by a base class pointer, its base class should have a virtual destructor.
If virtual, the derived class destructor gets called and then the base class destructor. If not virtual, only the base class destructor gets called.
Answered 2023-09-21 08:09:30