Question

I found a memory leak in my code that was caused by calling only the base class destructor for objects. This problem is understood: I already added the virtual to the destructor of the interface class MyÌnterface. What puzzles me is that the compiler obviously created a standard destructor for my helper class MyHelperthat is eventually called. I tried this with two different compilers.

This was very surprising to me, since I observed that most default implementations are not created if members or base classes introduce restrictions. Why is the protection of the destructor not inherited?

#include <iostream>

class MyInterface
{
public:
    virtual void doSomethingUseful()=0;
    // a lot more functions declared omitted
    virtual void doSomethingElse()=0;
    virtual void doSomethingIndividual()=0;
protected:
    /// protected destructor to forbid calling it on interfaces
    ~MyInterface() {} // HERE the virtual is clearly missing
};

/// a common base that defaults most functions implementations
class MyHelper: public MyInterface
{
public:
    void doSomethingUseful() {}
    // a lot more default implementations omitted
    void doSomethingElse() {}
};

class SomeImplementation: public MyHelper
{
public:
    SomeImplementation()
    {
        std::cout << "SomeImplementation ctr" << std::endl;
    }
    ~SomeImplementation()
    {
        std::cout << "SomeImplementation dtr" << std::endl;
    }
    void doSomethingIndividual()
    {
        std::cout << "SomeImplementation did it." << std::endl;
    }
};

/// user of MyInterface cannot delete object mi passed as parameter
int deleteSafeUsage(MyInterface& mi)
{
    mi.doSomethingIndividual();
    // would cause a compiler error: delete &mi;
}

/// usage restricted to MyHelper level, only exception is object creation
int testIt()
{
    MyHelper* h = new SomeImplementation;
    deleteSafeUsage(*h);
    delete h; // <- HERE the memory leak happens!
}

Here the output of the above example code, which "shows" the missing SomeImplementation ctr:

SomeImplementation ctr
SomeImplementation did it.
Was it helpful?

Solution

Constructors and destructors are not inherited. So why would their visibility be inherited?

You may want to check standard to be sure, but cppreference says this, emphasis mine:

If no user-defined destructor is provided for a class type (struct, class, or union), the compiler will always declare a destructor as an inline public member of its class.

So, if you want ~MyHelper to be protected, you must declare it explicitly.

Note, that if MyInterface had a virtual destructor, the implicit destructor of MyHelper would also be virtual. So this aspect is inherited, sort of. Again, you'll want to consult standard if you want to be sure, but this is mentioned in c++ faq lite

For completeness, here's Herb Sutters guidelines on how to use virtuality in general and with destructors.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top