C++ 在std::unique\u ptr<;中删除派生类;基地>;容器

C++ 在std::unique\u ptr<;中删除派生类;基地>;容器,c++,inheritance,c++11,destructor,unique-ptr,C++,Inheritance,C++11,Destructor,Unique Ptr,我有点困惑。基本上,我有两个不同的资源管理器(AudioLibrary和VideoLibrary),它们都继承自一个共享的BaseLibrary类。这个基类包含对音频和视频的引用。音频和视频都继承自名为Media的父类 我把数据保存在一张地图上,上面写满了独一无二的图片。但是,令我惊讶的是,我发现当这些指针最终通过.erase被删除时,只有媒体的基析构函数被调用 我想我错过了一些东西,但我认为编译器/运行时库会知道它要么指向视频,要么指向音频,并调用它的析构函数 情况似乎并非如此。我被迫这样做,

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

我把数据保存在一张地图上,上面写满了独一无二的图片。但是,令我惊讶的是,我发现当这些指针最终通过.erase被删除时,只有媒体的基析构函数被调用

我想我错过了一些东西,但我认为编译器/运行时库会知道它要么指向视频,要么指向音频,并调用它的析构函数

情况似乎并非如此。我被迫这样做,以实际回收我的所有资源:

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;
}
void AudioLibrary::deleteStream(const std::string和pathFile)
{
auto baseStream=mStreams[pathFile].release();
mStreams.erase(路径文件);
//重播!
自动aStream=静态_cast(基流);
删除aStream;
}
这是正常的行为吗

更新:


你很好——当然是析构函数缺少了“虚拟”性。我想我最近只是越来越少地思考虚拟和继承意味着什么,并且有点迷失在它的功能中,而不是概念本身。这种情况偶尔会发生在我身上。

这是因为您没有声明您的
媒体
析构函数
虚拟
。如您所见,如果您这样做,例如:

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());
}
struct媒体{
virtual~Media()=默认值;
};
结构音频库:媒体{};
结构视频库:媒体{};
int main(){
地图;
map[0]=std::unique_ptr(新音频库());
map[1]=std::unique_ptr(新视频库());
}


两个析构函数都将被调用。

唯一\u ptr的默认删除器是恰当命名的
default\u delete
。这是一个无状态函子,在其
T*
参数上调用
delete

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

使用函数指针deleter和captureless lambda可以很容易地做到这一点:

std::unique_ptr<B, void (*)(B *)> pb
    = std::unique_ptr<D, void (*)(B *)>(new D,
        [](B *p){ delete static_cast<D *>(p); });
std::unique\u ptr pb
=标准::唯一的ptr(新的D,
[](B*p){删除静态_cast(p);});
当然,这意味着您需要将deleter的模板参数添加到
unique\u ptr
的所有用法中。将其封装到另一个类中可能更为优雅


另一种方法是使用
shared\u ptr
,因为这确实捕获了派生类型,只要您使用
std::shared\u ptr(…)
或者最好使用
std::make\u shared(…)
创建派生的
shared\u ptr

“我想我错过了一些东西,但我认为编译器/运行时库会知道它要么指向视频,要么指向音频,并将其称为析构函数。“不,你缺少一个虚拟析构函数。你应该看看一本基本的C++ OOP书。你也可以搜索这个术语虚拟析构函数。你是否使基本析构函数
虚拟
?和/或的可能重复(其中一个是google上第一次点击的“派生类析构函数未调用”)未标记为重复:
unique_ptr
的情况与原始指针有点不同(尤其是可能的解决方案)。人们可能会认为智能指针有一个智能删除器,如
shared_ptr
does.@dyp它更通用(适用于多重继承等),并且安全性差异最小(因为
unique_ptr
模板化的移动构造函数正在做大部分工作)@dyp你说得对,我把自己弄糊涂了。一般情况下,你需要捕获原始指针及其删除器;当然,这就是
shared\u ptr
在别名时所做的。