删除智能指针指向的对象
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_ptr
或boost::shared_ptr
上调用reset()
将清除shared_ptr
,如果它是最后一个引用该对象的对象,则该对象将被删除,否则该对象仍然是共享的。
你确定没有重复使用这些指针吗?如果sEngine->addSoundSourceFromFile
两次返回相同的指针,它会被删除两次,因为您将它分配给两个不同的shared_ptr
s,它们彼此不知道。或者如果sEngine
在其中一个指针上调用delete,shared_ptr
将很难清理不再存在的内容。记下导致访问冲突的指针的值,并尝试在任何尝试释放该确切地址的删除处中断。
boost::shared_ptr
具有允许您确定 shared_ptr
是否唯一或获取其引用计数的成员函数。
@mark 我以为shared_ptr只有一种? (顺便说一句,我最初是用唯一的 ptrs 来做的,但它产生了同样的问题)。
@ArneMertz sEngine->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++编程经验:智能指针 -- 裸指针管得了的我要管,裸指针管不了的我更要管!