1

I understand that derived class is type compatible with a pointer to its base class. In the given sample code, new bar object construction takes place calling foo::foo() followed by bar::bar(). In the respective constructors, I am allocating resources to class members foo::int *a and bar::int *b.

Now I am initializing thus constructed object to base class type. With obj, I can call the base class destructor but not the derived class destructor. So, how can I deallocate the derived class resources in this case? Is this not a memory leak ?

#include <iostream>
class foo
{
    int *a;
public:
    foo()
    {
        a = new int[5];
        std::cout << "\n foo constructor" << std::endl;
    }
    ~foo()
    {
        std::cout << "\n foo destructor" << std::endl;
        delete[] a;
    }
};

class bar : public foo
{
    int *b;
public:
    bar()
    {
        b = new int[5];
        std::cout << "\n bar constructor" << std::endl;
    }
    ~bar()
    {
        std::cout << "\n bar destructor" << std::endl;
        delete[] b;
    }
};

int main()
{
    foo *obj = new bar; // Derived class object is type compatible with base class

    delete obj; // Equivalent to obj->~foo();
    return 0;
}

Thanks.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • 1
    Note that in real code, for any class that manages a resource, you must follow the Rule of Three and declare a destructor, copy constructor, and copy assignment operator. – James McNellis Jan 06 '11 at 07:30
  • http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three "Rule of Three" – Mahesh Jan 06 '11 at 07:35

4 Answers4

4

This is where the idea of the "virtual destructor" comes in. Technically speaking, if you delete an object through a pointer of a base class type, you must mark that base class destructor virtual or the result is undefined. If you do mark the destructor virtual, the meaning is different from other virtual functions. Instead of meaning "derived classes override this behavior," it means "when deleting this object through a base class pointer, call the derived destructors before calling the base constructor." This is the behavior you want.

As a general rule, any class you define that you plan on subclassing should have a virtual destructor to prevent this sort of problem.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
3

If you delete a derived class object via a pointer to one of its base classes, the base class destructor must be declared virtual, otherwise the behavior is undefined.

If ~foo() is declared virtual, you're good to go. ~bar() will be called first, then ~foo().

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • @James McNellis - By keeping the destructor `foo` virutal, I see both the destructors being called. But why is that now and not in the earlier case ? Thanks. – Mahesh Jan 06 '11 at 07:05
  • 1
    @Mahesh: In your original code, `~foo()` is not virtual and thus the call to the destructor is not dispatched dynamically (well, technically the behavior is undefined, but one common result is that only `~foo()` will be called, not `~bar()`). If you declare `~foo()` virtual then the destructor call will be dispatched dynamically and you get the correct, well-defined behavior. – James McNellis Jan 06 '11 at 07:06
  • I know that if a method declared virtual in the base class, then it is virtual in derived classes as well. Is the same true for destructors? – BЈовић Jan 06 '11 at 07:06
  • @VJo: Yes, the same is true for destructors. – James McNellis Jan 06 '11 at 07:07
  • @James : can you please specify the reference from the Standard where it says the behavior is undefined if `virtual` keyword is not used? – Nawaz Jan 06 '11 at 07:14
  • @Nawaz: 5.3.5/3 says: "In the first alternative (delete object), if the static type of the operand [of `delete`] 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." – James McNellis Jan 06 '11 at 07:17
  • @James : I just saw that in liaK's reply. :-) – Nawaz Jan 06 '11 at 07:22
3

It is actually an undefined behavior.

From Standard docs. 5.3.5.3 Delete,

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

liaK
  • 11,422
  • 11
  • 48
  • 73
  • In the above example, isn't `obj` dynamic type ? – Mahesh Jan 06 '11 at 07:18
  • 1
    @Mahesh: The static type of `*obj` is `foo`. The dynamic type of `*obj` is `bar`. Effectively, the static type is the type named in the code; the dynamic type is the actual type of the object. The two can be different, as demonstrated in your original code. – James McNellis Jan 06 '11 at 07:19
  • @James McNellis - I was about to ask the difference. You did it any how. Thanks. – Mahesh Jan 06 '11 at 07:22
0

Do this,

virtual ~foo()
{
   //your code
}

This ensures that doing delete *pFoo also invokes derived classes destructor (~bar()). The order of invocation would be ~bar() followed by ~foo().


It will also be good if you do the same for ~bar() also, that is,

virtual ~bar()
{
   //your code
}

Although it's not that much necessary for this scenario if you don't want to further derive from bar and want to use bar* for it's derived classes.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 2
    If `~foo()` is declared `virtual`, then `~bar()` is also `virtual`, regardless of whether it is declared with the `virtual` keyword. – James McNellis Jan 06 '11 at 07:13
  • 1
    To be clear, explicitly writing `virtual` before the definition of `~bar()` doesn't change anything -- it's automatically virtual because `~foo()` is virtual, and if another class `baz` is derived from `bar`, `~baz()` will automatically be virtual too. It's good documentation though. – j_random_hacker Jan 06 '11 at 07:14
  • @James McNellis - Does the same concept apply if I further derive a new class from `bar` ? – Mahesh Jan 06 '11 at 07:15