我有点困惑。基本上,我有两个不同的资源管理器(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。我想我最近只是认为虚拟和继承的含义越来越少,而且它的功能越来越少,而不是概念本身。偶尔会发生在我身上。
答案 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());
}
将调用两个析构函数。