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

时间:2014-06-05 12:40:26

标签: c++ inheritance c++11 destructor unique-ptr

我有点困惑。基本上,我有两个不同的资源管理器(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;
}

这是正常行为吗?

更新

你很好 - 当然它是析构函数中缺少的虚拟&#39; ness。我想我最近只是认为虚拟和继承的含义越来越少,而且它的功能越来越少,而不是概念本身。偶尔会发生在我身上。

2 个答案:

答案 0 :(得分: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); });

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

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

答案 1 :(得分:1)

这是因为您尚未声明Media析构函数virtual。如您所见,如果您这样做,例如:

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

将调用两个析构函数。

相关问题