删除std :: unique_ptr <Base>容器中的派生类

Gaz*_*zoo 2 c++ inheritance destructor unique-ptr c++11

我有点困惑.基本上,我有两个不同的资源管理器(AudioLibrary和VideoLibrary),它们都从共享的BaseLibrary类继承.此基类包含对音频和视频的引用.音频和视频都从名为Media的父类继承.

我将数据保存在地图中,填充了unique_ptr.但令我惊讶的是,我发现当这些指针最终通过.erase被删除时,只调用了Media的基本析构函数.

我想我错过了一些东西,但我认为编译器/运行时库会知道它指向视频或音频并将其称为析构函数.

似乎并非如此.我被迫做这样的事情来实际收回我的所有资源:

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;
}
Run Code Online (Sandbox Code Playgroud)

这是正常的行为吗?

更新:

你没事 - 当然这是析构函数缺失的'虚拟'.我想我最近只是想到虚拟和继承意味着什么越来越少,并且让我的头脑失去了它的功能,而不是概念本身.偶尔会发生在我身上.

eca*_*mur 7

默认删除器unique_ptr<T>是适当命名的default_delete<T>.这是一个无状态的函子,可以调用delete它的T *参数.

如果希望在销毁unique_ptr基类时调用正确的析构函数,则必须使用虚拟析构函数,或者在删除器中捕获派生类型.

您可以使用函数指针删除器和无捕获的lambda轻松完成此操作:

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

当然,这意味着您需要将删除器的模板参数添加到所有用途unique_ptr.将其封装在另一个类中可能更优雅.

另一种方法是使用shared_ptr,因为它捕获派生类型,只要您shared_ptr使用std::shared_ptr<D>(...)或最好创建派生类型std::make_shared<D>(...).