0

In my application I used to have a unique_ptr<parent> _member as a member of a custom class. The code worked fine. However when I recently tried to create another class that inherit from the parent class, and initialize _member using _member = unique_ptr<child>(new child()) in the constructor, I realize that when the custom class got destroyed, the destructor of _member only calls the parent destructor but now the child destructor.

This behavior makes sense to me. Because afterall _member is of type unique_ptr<parent>. However I'm wondering what are the options for me to call the child destructor given only _member.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294

3 Answers3

3

The behavior does not make sense. If child's destructor needs to free memory, and it is not called, you will leak memory.

Parent needs to have a virtual destructor, to allow the destructors of derived classes to be called, when deleting them through a pointer to the parent.

Rule of thumb: If base class has at least one virtual function, it should have a virtual destructor.

Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
  • 2
    +1, although the rule I follow is a bit more involved: if there is a virtual function, the type should have either a public virtual destructor or a protected non-virtual destructor. Both solve the same issue: code deleting through a pointer to base. – David Rodríguez - dribeas Jun 30 '14 at 03:10
  • @DavidRodríguez-dribeas That's a good rule! – Neil Kirk Jun 30 '14 at 09:16
2

In addition to Neil's fine answer, there's another route you could take.

If you were to use a std::shared_ptr instead of a std::unique_ptr you would gain the benefits of std::shared_ptr's type erasure features. Let's look at an example:

#include <memory>
#include <iostream>

struct X {
    ~X() {
        std::cout << __PRETTY_FUNCTION__ << "\n";
    }
};

struct Y : X {
    ~Y() {
        std::cout << __PRETTY_FUNCTION__ << "\n";
    }
};

int main() {
    std::shared_ptr<X> obj = std::make_shared<Y>();
}

This code, when run will output:

Y::~Y()
X::~X()
Bill Lynch
  • 80,138
  • 16
  • 128
  • 173
0

I would make the parent class destructor virtual. On the off chance you have a use case that requires a class with no virtual functions to be destroyed properly and you don't want to use std::shared_ptr for its type-erased destruction feature, you can achieve the same thing with unique_ptr and a custom deleter:

template <typename T>
void delete_function(void* ptr) {
    // Tell GCC that we know we're doing something stupid and
    // it need not warn us about it.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
    delete static_cast<T*>(ptr);
#pragma GCC diagnostic pop
}

template <typename T>
using erased_unique_ptr = std::unique_ptr<T, void(*)(void*)>;

template <typename T, typename... Args>
inline erased_unique_ptr<T>
make_unique_erased(Args&&... args) {
    return erased_unique_ptr<T>(new T(std::forward<Args>(args)...), &delete_function<T>);
}

See it live at Coliru.

Casey
  • 41,449
  • 7
  • 95
  • 125