3

I have a basic interface (using Microsofts C++ syntax for interfaces in Visual Studio 2013) which exposes simple functions like this:

__interface IDisposable {
    void Dispose();
};
__interface IBase : public IDisposable {
    void Foo();
    void Bar();
};

There is a specific type of classes which has to inherit those methods thus these classes have this structure:

class Derived : public IBase {
public:
    Derived();
    ~Derived();
    void Dispose();
    void Foo();
    void Bar();
}

These classes do have some variables as well and I wanted to use smart pointers but those pointers did produce leaks because the destructor ~Derived() isn't called in the following scenario:

  • I am having a Stack<IBase*> which handles a bunch of those objects (e.g. Derived, Derived2, etc.).
  • Once the top-most element (pCurrent) should be removed from the stack with the corresponding data (m_Data) I am calling the method to dispose resources: ((IDisposable*)pCurrent->m_Data)->Dispose();
  • This call is followed by a delete pCurrent->m_Data which apparently tries to invoke ~IBase() which doesn't exist obviously as it is an interface. Therefore the mentioned ~Derived() won't be called as well and any smart pointers in Derived would not be deleted as well and this causes serious memory leaks.

Surprisingly a manual deletion works:

auto p = new Derived();
delete p; // ~Derived() is properly called as we are not handling a IBase* object

I was thinking about using a virtual destructor, but there is no way to define destructors in those interfaces to make ~Derived() virtual as well.

Is there a better approch to be able to call the right destructors?

Christian Ivicevic
  • 10,071
  • 7
  • 39
  • 74
  • Have you tried having a virtual destructor? – dtech Mar 23 '15 at 14:45
  • @ddriver Even with `virtual ~Derived()` this function isn't called in the described scenario. – Christian Ivicevic Mar 23 '15 at 14:47
  • You should have virtual destruction from the very bottom of the polymorphic hierarchy and the interfaces as well. – dtech Mar 23 '15 at 14:48
  • @ddriver This came to my mind as well yet the [documentation](https://msdn.microsoft.com/en-us/library/50h7kwtb.aspx) mentions that an interface _Cannot contain constructors, destructors, or operators._ Does this mean that I have to rethink the hierarchy and make abstract base class with pure virtual functions instead of using the `__interface` keyword to simplify the code? – Christian Ivicevic Mar 23 '15 at 14:52
  • Sure it can, there is no such thing as an interface in C++, you should scrap that keyword, that's MS BS. It is just classes, and as such they can have both constructors and destructors, even if some of their methods are abstract and therefore the actual "interface" class is abstract too. – dtech Mar 23 '15 at 14:54
  • That might just be syntax sugar for a `struct`, seeing how it has public access by default. Either way anyway. – dtech Mar 23 '15 at 14:59
  • possible duplicate of [When to use virtual destructors?](http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors) – Mgetz Mar 23 '15 at 18:13

2 Answers2

5

The problem as I mentioned in the comments is that you don't have a virtual destructor in the bottom of your polymorphic hierarchy, therefore you get undefined behavior when you delete IBase pointers.

It works in your isolated example because auto in that case infers a Derived * type, so the appropriate destructor is called in this "static context".

But in general, you should consider that according to the standard, deleting a base class pointer that has no virtual destructor is UNDEFINED BEHAVIOR. That means "anything" can happen, what usually ends up happening is calling the destructor for the base class type, which means for every other type in the hierarchy you are not getting the right destruction behavior.

As for using MS's __interface - don't do it unless you really have to, then you won't have to deal with the limitation it imposes. I'd recommend you keep away from MS's language "extensions" and stick to good old standard and portable C++, which works with every compiler out there. There are no interfaces in C++ anyway.

You should design your hierarchy so that you absolutely do have a virtual destructor, if not at the very bottom, then at least at the "base level" pointers you will be using. Either make IBase a class with virtual destructor, or make a class Base : public IBase that has one, and use that as your "polymorphic root".

struct A {
  ~A() { std::cout << "destroying A" << std::endl; }
};

struct Base {
  ~Base() { std::cout << "destroying Base" << std::endl; }
};

struct Derived : Base {
  ~Derived() { std::cout << "destroying Derived" << std::endl; }
  A a;
};

And then:

  Base * p = new Derived;
  delete p; // destroying Base - bad bad, Derived is not destroyed, neither is A

But it only takes the changing of ~Base() to be virtual and you get the expected output:

destroying Derived
destroying A
destroying Base

Also generally, you don't base (as in "use for foundation") classes on interfaces, just to extend them.

The thing is that when you have a virtual destructor at the polymorphic level you are working on, it gets invoked from the vtable, meaning it will call the appropriate destructor for each unique type, since each unique type has its own dedicated vtable. If you don't have a virtual destructor, the standard says "undefined behavior" which leaves the choice to different compiler vendors to do as they please, and they end up doing the most logical thing - call the destructor for the type of the pointer. You couldn't ask for more... and yet it is not desirable, because you end up skipping all the destruction for every member or inherited type on top of the base type.

dtech
  • 47,916
  • 17
  • 112
  • 190
0

The usual way to do this is to make whatever type you are calling delete on to have a virtual destructor, as in:

virtual ~IDisposable() = default;

All derived classes will also have a virtual destructor because of the rules of C++.

My recommendation is to also store the result in a unique_ptr so that you do not have to call delete yourself, as in:

std::unique_ptr<IDisposable> p(new Derived());

A less intrusive method is, instead of declaring the destructor virtual, you can use a shared_ptr to hold the object, because that uses a different method (templated constructor and type erasure to keep track of the actual object type) to correctly call the destructor, as in:

std::shared_ptr<IDisposable> p = std::make_shared<Derived>();

As I'm not sure what the __interface keyword does (that is Microsoft-specific), you may need to do it that way, but the virtual destructor with unique_ptr solution is preferred, as that doesn't add reference counting into the mix.

Nevin
  • 4,595
  • 18
  • 24