2

I am new to C++ and would like to know if the use of a virtual destructor is required in the following program:

#include <iostream>

class Shape2D {
protected:
    int firstDimension;
    int secondDimension;
public:
    Shape2D(int a, int b) {
        firstDimension = a;
        secondDimension = b;
    }
    virtual void getArea() {
        std::cout << "Area undefined." << std::endl;
    }
};

class Rectangle : public Shape2D {
public:
    Rectangle(int length=0, int width=0) : Shape2D(length, width) { 
    }
    void getArea() {
        int area = firstDimension*secondDimension;
        std::cout << "Area of rectangle: " << area << " sq units." << std::endl;
    }
};

int main()
{
    Rectangle rct(4, 5); // stack allocated derived object
    Shape2D *sh = &rct; // base pointer to derived object
    sh->getArea();

    // object goes out of scope here.
}

I was reading this post and the most upvoted answer said that the use of a virtual destructor is suitable whenever we want to delete a derived object allocated by new through its base pointer. However, in this context, we are allocating a derived object Rectangle on the stack in the main routine and the base pointer is not being delete-ed so would it matter if a virtual destructor is provided?

First User
  • 704
  • 5
  • 12
  • 1
    The answer you link to is correct. Your code has no `delete` at all. When your `Rectangle` is destroyed, the compiler knows it's a `Rectangle`. You do not need a virtual destructor. – Drew Dormann Feb 15 '23 at 22:55
  • 4
    You are probably correct in your considerations: in your example the compiler knows the type of your derived object and will call the respective destructor. The "but" in this is, that - in real code - you never know how your classes are gonna be used. So in real code you always should provide virtual destructors in the base classes. – kaba Feb 15 '23 at 23:00
  • 3
    Also, once you have a `virtual` method you already paid for the vtable, and in case of non-polymorphic destruction like this there's no dynamic dispatch, so to adding it "just in case" someone in future will `delete` through a base class pointer is essentially free. – Matteo Italia Feb 15 '23 at 23:06
  • @kaba -- of course you know how your classes are going to be used. That's called **design** and **documentation**. If your class does not have a virtual destructor its documentation should say so; that way users will know that is not suitable for use as a base class for objects of derived types allocated on the free store. Programming is about **understanding** what you're doing, not throwing things together and hoping that the result works more or less correctly. – Pete Becker Feb 15 '23 at 23:11
  • 2
    A `virtual` destructor is only needed to avoid undefined behaviour using a `delete` expression. However, rather than simply not having a `virtual` destructor in your case (and documenting), consider whether some *future* usage of your class (by you or someone else) may dynamically allocate/release an object (`new` and `delete`) or whether you have taken steps to prevent that - preferably at compile time. Programmers often don't read documentation closely enough to pick up such things, unless their compiler squawks. – Peter Feb 15 '23 at 23:28

2 Answers2

2

Since the instance of the derived object rct is on the stack and not dynamically allocated using new, there is no need for a virtual destructor in the base. This is specific to your exact code though and not a general rule.

The purpose of a virtual destructor is to make sure that the destructor of the derived class is called when a derived object is deleted through a base class pointer. This is important when the derived class destructor has to do extra work like releasing resources, e.g. closing files, freeing memory, etc.

It is generally considered good practice to provide a virtual destructor in a base class if it is intended to be used as a polymorphic base class, and derived objects are expected to be allocated dynamically. There are other options like marking you non-virtual destructor as private or protected which will result in compile errors when you try to delete through the base.

If you intended to delete via the base class then it necessary to have a virtual destructor to avoid undefined behavior.

C++ Standard (ISO/IEC 14882:2017), deleting an object through a pointer to a base class with a non-virtual destructor is undefined behavior. Specifically, section 5.3.5, "Delete" states:

"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."

If you know that your code will not be extended by 3rd party's then you might consider the alternative approach using std::variant or similar construct if available to you.

Pete Fordham
  • 2,278
  • 16
  • 25
  • "It is always a good practice" ⇒ no. It is only one of the possible approaches. You can also make the destructor non-virtual `protected` if deletion is not intended to be part of the interface. – spectras Feb 16 '23 at 00:21
1

A virtual destructor is not required, but neither is a public destructor. It is a good practice in this case to remove the public destructor (by making it protected).

protected:
  ~Shape2D() = default;
Etienne Laurin
  • 6,731
  • 2
  • 27
  • 31
  • If you delete the destructor you can't make the object at all. – Mark Ransom Feb 15 '23 at 23:58
  • @PeteFordham — an abstract class is a class with one or more pure virtual functions. Full stop. If it needs to be constructed it needs to be constructed. For example, it could have a data member of type `std::string`. – Pete Becker Feb 16 '23 at 01:11