Virtual Destructors

Hello! I have a question regarding virtual destructors. When we inherit from a base class with a public access specifier, all public and protected fields and methods excluding constructors of the base class are copied into the derived class. It's been my understanding that virtual destructors are used to ensure that a derived class' destructor is called before the object is deleted from memory.

So, with that in mind, would I want to make all my destructors virtual in an inheritance situation or only if I wanted the destructor of a derived class to be called? I've read on several articles that it's a good rule of thumb to have a virtual destructor when using inheritance. However, to me this felt a bit unnecessary as my derived classes do not allocate on the heap so I don't necessarily mind if their destructors weren't called, or am I missing something else here? I hope someone can clarify this for me and I'm very thankful for your help.

Cheers :)
> I've read on several articles that it's a good rule of thumb to have a virtual destructor when using inheritance.

Yes. It is a safe option.
And to avoid inadvertent slicing, declare the copy operations as deleted.


> However, to me this felt a bit unnecessary as my derived classes do not allocate on the heap

Yes, it is unnecessary if the there are not going to be any objects with dynamic storage duration;
in this case, declare the base class destructor as protected and non-virtul.
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-dtor-virtual
Hi, thank you for clearing that up for me, I think that document you linked will be very helpful to me as well. May I ask what inadvertent slicing mean?
Last edited on
Hehe I'm learning a lot from you, thanks ^^
with a public access specifier, all public and protected fields and methods excluding constructors of the base class are copied into the derived class.

No, even private data members and methods are copied, hence you can use them within the class. Constructors and Destructors are no different from your other methods, they follow the same rules.

It's been my understanding that virtual destructors are used to ensure that a derived class' destructor is called before the object is deleted from memory.

Only in case of dynamic allocation and when you're using a parent class type pointer to point to the dynamically allocated memory.

Yes, it is unnecessary if the there are not going to be any objects with dynamic storage duration;
in this case, declare the base class destructor as protected and non-virtul.

JLBorges is there any reason to use protected over public for the destructor?

Anyways Angela, do note that if you want to instantiate an object of the parent class then your destructor must be public. And also note that you cannot have a pointer of parent point to dynamically allocated memory if your destructor is protected.
Is there any reason to use protected over public for the destructor?

Herb Sutter's GotW #18
http://www.gotw.ca/publications/mill18.htm

In particular, see the section subtitled Guideline #4.
Last edited on
Ah okay so it's to avoid accidental bugs. But the destructor must be public if the parent can have its own separate object.
I ran into a problem using protected destructor for my base class where I couldn't free memory because I couldn't access the base class' destructor. Here's a simplified example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Shape
{
public:
	static Shape* CreateShape(unsigned short int index)
	{
		switch (index)
		{
		case 0: return new Triangle(); break;
		case 1: return new Rectangle(); break;
		default: break;
		}
	}
	// Some other things here 
protected:
	~Shape() = default;
};

class Triangle : public Shape
{
public:
	Triangle()
	{
		// Do some things 
	}
};

class Rectangle : public Shape
{
public:
	Rectangle()
	{
		// Do some things 
	}
};

int main()
{
	enum E_SHAPES : unsigned char
	{
		E_TRIANGLE,
		E_RECTANGLE,
		E_TOTAL_SHAPES
	};

	Shape* triangle = Shape::CreateShape(E_TRIANGLE);
	// Okay do some cool things with triangle now
        // ...Few lines later...
	// We don't need triangle anymore, deallocate memory!
	delete triangle; // <-- Yields error: "Shape::~Shape is inaccessible"
}


So I reverted back to public destructor.
Here you do need a public virtual destructor.
Deleting an object through pointer to base invokes undefined behavior unless the destructor in the base class is virtual. https://en.cppreference.com/w/cpp/language/destructor#Virtual_destructors
Last edited on
I ran into a problem using protected destructor for my base class where I couldn't free memory because I couldn't access the base class' destructor.

That is a good thing because you used it incorrectly. Since the Shape destructor is not virtual it would be wrong to delete a Triangle object through a Shape pointer.
Can I ask why CreateShape is declared as a static method inside the class Shape when it's not meant to be called by a class object?

Also you must always make sure that the function must always return a value or that can lead to undefined behavior. So maybe return a NULL as default.

(BTW: You don't need to write a default if you're going to write 'break;' under it, the switch is exited if none of the cases match)
Okay so if I'm understanding everyone correctly, I could not delete the derived object through pointer to base because it would produce an undefined behavior. The article does not explain why or how this produces an undefined behavior, it would of been helpful if it did. By moving the destructor into the public region, the problem goes away and I can delete the derived object. I'm still not completely convinced as to why I'd want to prefix my destructor with virtual in this case because I'm not allocating any memory on the heap in any of my derived classes. However, I am going to make my destructor virtual since this seems to be the more suitable approach.

Grime, the idea is to have Shape act as an abstract class and use it to create shapes from it without having to instantiate shape objects. Instead I just use it to create my shapes through some polymorphic magic then delete it once the vertices are loaded in the vRAM. I am very aware that this may not be the best way to do this but I'm still kind of learning as I go. Also you're right, I should be returning a nullptr or some default shape in the function.
No, by moving the destructor to public you are not calling the destructor of the derived class, you are calling the destructor of only the base class.

Google about 'virtual tables' or something like that (just google 'how is polymorphism implemented' or something like that and you should get material), they will tell you how polymorphism is implemented.

In short though, it's important to make the destructor virtual so that the compiler knows to look for a destructor in the derived class.
I know how virtual work, I just wanted to clarify. If I can avoid using virtual then I'd rather avoid it if I can as it has a little bit of overhead associated with it. If I am working on an embedded system where memory capacity is small, every bit of optimization can make a big difference.
A pointer of a base class thinks that it's pointing to the base class. So if you're actually pointing to a a dynamically allocated derived class object and called delete on the pointer, the pointer would call the destructor of the base class. This is for pointers to dynamically allocated memory.

But if you had declared the base class destructor as being virtual, then the compiler will look in the virtual table to call the appropriate destructor. And since we've defined the destructor in the derived class, the compiler will find the correct destructor for the derived class (also consists of destructor of base class).
This is what the standard says.

C++17 ยง8.5.3/3 [expr.delete]
... 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 example posted above the static type is Shape and the dynamic type is Triangle. The first condition ("the static type shall be a base class of the dynamic type") is fulfilled, but the second condition ("the static type shall have a virtual destructor") is not, hence the behaviour is undefined.
Last edited on
Alright, thank you all for the discussion :)
Topic archived. No new replies allowed.