Deleting derived classes in std::unique_ptr<Base> containers

青春壹個敷衍的年華 提交于 2019-12-30 07:31:35

问题


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.


回答1:


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




回答2:


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.



来源:https://stackoverflow.com/questions/24060497/deleting-derived-classes-in-stdunique-ptrbase-containers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!