删除智能指针指向的对象

Posted

技术标签:

【中文标题】删除智能指针指向的对象【英文标题】:Deleting objects pointed to by smart pointers 【发布时间】:2013-04-17 14:51:58 【问题描述】:

在我的代码中,我有一个 SoundManager 类,它包含并操作我的游戏的所有声音。这个类需要被实例化,它的方法被多个其他类调用。但是我希望只有一组声音占用内存,所以为了提高效率,所有资产都被声明为静态 shared_ptrs。

#include "SoundManager.h"

static shared_ptr<ISoundEngine> sEngine;

static shared_ptr<ISoundSource> hoverSound;
static shared_ptr<ISoundSource> confirmSound;
static shared_ptr<ISoundSource> mainBGM;
static shared_ptr<ISound> bgmInterface;

SoundManager::SoundManager(void)



//first we need to create the irrKlang sound engine instance
    if(!sEngine)
    
        sEngine.reset(createIrrKlangDevice());
    


    if(!hoverSound)hoverSound.reset(sEngine->addSoundSourceFromFile("Sounds/ButtonHover.mp3"));
    if(!confirmSound)confirmSound.reset(sEngine->addSoundSourceFromFile("Sounds/ButtonConfirm.mp3"));
    if(!mainBGM)mainBGM.reset(sEngine->addSoundSourceFromFile("Sounds/mainBGM.mp3"));


    //set some default volumes
    hoverSound->setDefaultVolume(1.0f);
    confirmSound->setDefaultVolume(0.4f);
    mainBGM->setDefaultVolume(0.5f);





SoundManager::~SoundManager(void)
   

这个 SoundManager 在我的 main() 函数中实例化,并且每次我需要加载标题屏幕(SoundManager 也在这个标题屏幕类中实例化)。一遍又一遍地初始化和销毁​​标题屏幕不会导致问题。静态 shared_ptrs 对象不会被销毁,因为它们仍在被 SoundManager 的主要函数实例使用。

现在这一切在运行我的游戏时运行良好。然而,当谈到干净地退出时,当上面的静态对象被拆除时,未处理的运行时异常(访问冲突)被抛出给我。 VS2012 的调试器将我指向 memory.h 中的一行。

private:
    virtual void _Destroy()
           // destroy managed resource
        delete _Ptr;       <<<<<<<<<The debugger points to this line
        

我的理解是,与 obj-c 类似,c++ shared_ptrs 使用引用计数器来确保对象不会被删除,直到不再存在需要使用它们的对象。我不明白是什么导致了这些错误。

也许我不应该省略一个重要部分:我的游戏是通过调用 exit(0) 退出的;尽可能靠近 main() 函数。在这样做之前,我没有采取任何行动来清理 SoundManagers 成员,因为我认为 shared_ptr 已经处理了这个问题。

有人知道是什么导致了我的清理问题吗?

【问题讨论】:

它们是什么类型的 shared_ptr?在std::shared_ptrboost::shared_ptr 上调用reset() 将清除shared_ptr,如果它是最后一个引用该对象的对象,则该对象将被删除,否则该对象仍然是共享的。 你确定没有重复使用这些指针吗?如果sEngine-&gt;addSoundSourceFromFile 两次返回相同的指针,它会被删除两次,因为您将它分配给两个不同的shared_ptrs,它们彼此不知道。或者如果sEngine 在其中一个指针上调用delete,shared_ptr 将很难清理不再存在的内容。记下导致访问冲突的指针的值,并尝试在任何尝试释放该确切地址的删除处中断。 boost::shared_ptr 具有允许您确定 shared_ptr 是否唯一或获取其引用计数的成员函数。 @mark 我以为shared_ptr只有一种? (顺便说一句,我最初是用唯一的 ptrs 来做的,但它产生了同样的问题)。 @ArneMertz sEngine-&gt;addSoundSourceFromFile() 返回一个指向 ISoundSource 类型的新对象的指针。我决定创建指针 shared_ptrs 因为它们是静态的,并且我只希望在整个程序中它们指向的对象的一个​​实例,同时仍然具有 SoundManager 类的多个实例。 sEngine 或任何与此相关的对象是否有可能获得最初由 shared_ptr 管理的指针的所有权并将其删除,而原始拥有的 shared_ptr 对此有什么要说的?除非我在一个愚蠢的地方调用 reset()? 【参考方案1】:

如果您想手动释放shared_ptr 使用的资源,您需要调用reset。至于使用静态shared_ptr,我想我不明白。关键是它们不会复制周围的资源,因此您将只有一个资源。

【讨论】:

我决定将指针设为 shared_ptrs,因为它们是静态的,我只希望在整个程序中存在它们指向的对象的一个​​实例,同时仍然具有 SoundManager 类的多个实例.但是,仅将它们设为“shared_ptr”并不能做到这一点,这就是我将它们设为静态的原因。这能解释我的推理吗?【参考方案2】:

您正在使用 IRRKLang 库。该库以预编译二进制文件的形式提供(如果您在 Windows 上,则为 dll)。该库通过使用纯虚拟库使其自身二进制兼容。这可行,但是您不能像这样删除库的对象,因为您的程序 new/delete 与库的 new/delete 不同。这些类型的库提供了一种释放内存的方法,这种情况下会丢弃。

要使用 shared_ptr 等,您必须使用自定义删除器。查看Using custom deleter with std::shared_ptr 了解如何操作并根据自己的需要进行修改。

在您的情况下,由于您使用的是 Visual Studio 2012,因此您可能可以这样做

template<class T>
struct IrrDeleter
   void operator()(T* t)
       t->drop();
   
;

然后更改所有重置行以包括删除器,例如

sEngine.reset(createIrrKlangDevice(),IrrDeleter<ISoundEngine>());

【讨论】:

谢谢,我想这就是我需要的。那么你是说 shared_ptrs 删除它所持有的数据的方式与 IRRKlang 的 ISoundEngine 在超出范围时管理它自己的内存的方式冲突? 在某种程度上。您的删除(即使在 shared_ptr 之外手动删除)对 ISoundEngine 不起作用。那是因为你的 delete 和 IrrKlang delete 是不同的,因为 IrrKlang 可能是用不同的编译器/标准库编译的。从 IRefCounted 派生的所有 Irrklang 类都提供了可以正确删除它的 drop 方法

以上是关于删除智能指针指向的对象的主要内容,如果未能解决你的问题,请参考以下文章

C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!

C++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!

智能指针

动态内存与智能指针

智能指针类模板

C++11:智能指针