Jeremy Hansen
Jeremy Hansen

Reputation: 43

Is it necessary to have virtual destructor if the derived class only contains automatic variable members?

struct base
{
    base(){}
    ~base() { cout << "base destructor" << endl; }
};

struct derived : public base
{
    derived() : base() { vec.resize(200000000); }
    ~derived() { cout << "derived destructor" << endl; }
    vector<int> vec;
};

int main()
{
    base* ptr = new derived();
    delete ptr;

    while (true)
    {

    }
}

The above code leaks due to delete operation not calling derived object's destructor. But...

struct base
{
    base() {}
    ~base() { cout << "base destructor" << endl; }
};

struct derived : public base
{
    derived() : base() {}
    ~derived() { cout << "derived destructor" << endl; }
    int arr[200000000];
};

int main()
{
    base* ptr = new derived();
    delete ptr;

    while (true)
    {

    }
}

In second case, the memory doesn't leak despite the base destructor is only being called. So I'm assuming it's safe to not have a base destructor if all my members are automatic variables? Doesn't 'arr' member in derived class never go out of scope when derived object's destructor is not being called? What's going on behind the scenes?

Upvotes: 0

Views: 396

Answers (3)

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385108

YES!

I see that you are thinking "practically", about what destructions might be missed. Consider that the destructor of your derived class is not just the destructor body you write — in this context you also need to consider member destruction, and your suggestion may fail to destroy the vector (because the routine non-virtually destroying your object won't even know that there is a derived part to consider). The vector has dynamically allocated contents which would be leaked.

However we don't even need to go that far. The behaviour of your program is undefined, period, end of story. The optimiser can make assumptions based on your code being valid. If it's not, you can and should expect strange sh!t to happen that may not fit with how your expectation of a computer should work. That's because C++ is an abstraction, compilation is complex, and you made a contract with the language.

Upvotes: 3

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14589

It's not necessary to have a memory leak and still invoke an UB. Memory leak is a kind of expected UB if your derived class isn't trivial. Example:

#include <iostream>

class  Field {
public:
    int *data;
    Field() : data(new int[100]) {} 
    ~Field() { delete[] data; std::cout << "Field is destroyed"; }
};

class Base {
    int c;
};

// Derived class, contains a non-trivial non-static member                 
class Core : public Base 
{
    Field A;
};

int main()
{
    Base *base = new Core;
    delete base;  // won't delete Field
}

he C++ Standard, [expr.delete], paragraph 3 states (2014 edition)

In the first alternative (delete object), 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 second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

In reality , if base class is trivial, all fields are trivial and derived class contains no non-static or non-trivial members, one might argue, that those classes are equal, but I'm yet to find way how to prove that through standard.It's likely an IB instead of UB.

Upvotes: 1

eerorika
eerorika

Reputation: 238311

It is always necessary to have a virtual destructor in a base class if a derived object is ever deleted through a pointer to that base. Otherwise behaviour of the program is undefined. In any other case it is not necessary to have a virtual destructor. It is irrelevant what members the class has.

Upvotes: 3

Related Questions