3

I'm a little confused. Basically, I've got 2 different resource managers (AudioLibrary and VideoLibrary) that both inherit from a shared BaseLibrary class. This base class contains references to both audio and video. Both audio and video inherit from a parent class called Media.

I'm keeping the data in a map, filled with unique_ptr. But, to my surprise, I've discovered when these pointers are eventually deleted via .erase, only the base destructor for Media is called.

I guess I've missed something, but I thought that the compiler/run-time library would know that it's either pointing to a video or audio and call it's destructor.

Seems not to be the case. I'm forced to do something like this to actually reclaim all my resources:

void AudioLibrary::deleteStream( const std::string &pathFile ) { auto baseStream = mStreams[ pathFile ].release(); mStreams.erase( pathFile ); // Re-cast! auto aStream = static_cast<AudioStream*>( baseStream ); delete aStream; } 

Is this normal behavior?

Update:

You're all right - of course it is the missing 'virtual'ness of the destructor. I guess I've recently just thought less and less of what virtual and inheritance means and kind of gotten my head lost in its functionality, rather than the concepts themselves. It happens for me occasionally.

4
  • 4
    "I guess I've missed something, but I thought that the compiler/run-time library would know that it's either pointing to a video or audio and call it's destructor." No. You're missing a virtual destructor. You should probably look into a basic C++ OOP book. You could also search for that term virtual destructor. Commented Jun 5, 2014 at 12:45
  • did you make the base destructor virtual? Commented Jun 5, 2014 at 12:46
  • Possible duplicate of stackoverflow.com/q/1315534 and/or stackoverflow.com/q/20573587 (one of them is the first hit on google for "derived class destructor not called") Commented Jun 5, 2014 at 12:48
  • have unmarked as duplicate: the case of unique_ptr is a bit different to a raw pointer (and especially, the possible solutions). One might expect that the smart pointer has a smart deleter, like shared_ptr does. Commented Aug 26, 2016 at 1:22

2 Answers 2

8

The default deleter for unique_ptr<T> is the aptly named default_delete<T>. This is a stateless functor that calls delete on its T * argument.

If you want the correct destructor to be called when a unique_ptr to a base class is destructed, you must either use a virtual destructor, or capture the derived type in a deleter.

You can do this quite easily using a function pointer deleter and captureless lambda:

std::unique_ptr<B, void (*)(B *)> pb = std::unique_ptr<D, void (*)(B *)>(new D, [](B *p){ delete static_cast<D *>(p); }); 

Of course, this means that you need to add the template argument for your deleter to all uses of unique_ptr. Encapsulating this in another class might be more elegant.

An alternative to this is to use shared_ptr, as that does capture the derived type, as long as you create the derived shared_ptr using std::shared_ptr<D>(...) or, preferably, std::make_shared<D>(...).

Sign up to request clarification or add additional context in comments.

2 Comments

@dyp it's more general (works with multiple inheritance etc.) and the difference in safety is minimal (as unique_ptr templated move constructor is doing most of the work anyway).
@dyp you're right, I got myself confused. In the general case you'd need to capture the original pointer as well as its deleter; of course, this is what shared_ptr does when aliasing.
3

This is due to the fact that you haven't declared your Media destructor virtual. As you can see, if you do, for example:

struct Media { virtual ~Media() = default; }; struct AudioLibrary : Media {}; struct VideoLibrary : Media {}; int main() { std::map<int, std::unique_ptr<Media>> map; map[0] = std::unique_ptr<Media>(new AudioLibrary()); map[1] = std::unique_ptr<Media>(new VideoLibrary()); } 

demo

both destructors will be called.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.