我正在实现一个单例模式。在这里,我在GetInstance中创建一个新的Singleton *实例,当我尝试在析构函数中删除它时,它在无限循环中执行。在这种情况下如何避免内存泄漏?
请参考以下代码:
#define NULL 0
class Singleton
{
private :
static Singleton* m_pInstance;
Singleton(){};
public :
static Singleton* GetInstance()
{
if(m_pInstance == NULL)
{
m_pInstance = new Singleton();
}
return m_pInstance;
}
~Singleton()
{
//delete m_pInstance; // The system goes in infinate loop here if i uncomment this
m_pInstance = NULL;
}
};
Singleton* Singleton ::m_pInstance = NULL;
int main()
{
Singleton* pInstance = Singleton::GetInstance();
delete pInstance;
}
答案 0 :(得分:33)
当然会导致无限循环!
你调用析构函数,但是析构函数也调用析构函数,因此析构函数再次调用析构函数......再次...
如果要使用delete
,则必须在析构函数的外部中使用它,而不是在析构函数中再次调用它。
为此,您可以使用另一个静态方法来镜像GetInstance()
方法:
class Singleton
{
public :
...
// this method is a mirror of GetInstance
static void ResetInstance()
{
delete m_pInstance; // REM : it works even if the pointer is NULL (does nothing then)
m_pInstance = NULL; // so GetInstance will still work.
}
...
~Singleton()
{
// do destructor stuff : free allocated resources if any.
...
}
注意:其他人警告你使用单身人士是正确的,因为这种模式经常被滥用。所以在使用之前要先思考但无论如何,请继续,这是学习的好方法!
答案 1 :(得分:20)
虽然最佳做法是在大多数情况下不使用单例模式,但最好在函数中使用静态局部变量来创建单例:
static Singleton& Singleton::GetInstance() {
static Singleton the_singleton;
return the_singleton;
}
为最佳实践提供一些理由:除非您必须代表真正的全球资源,否则通常不需要单一语言。单身人士遭受全局变量的所有缺点(因为他们是具有一些OO结冰的全局变量),并且通常没有理由成为真正的单数。天真的程序员可能希望将God
实现为单例对象。当顾客变成多神教徒时,明智的程序员不会也会欢喜。
答案 2 :(得分:11)
这是一个更正确的单例实现:
class Singleton
{
public:
static Singleton& Instance()
{
static Singleton inst;
return inst;
}
protected:
Singleton(); // Prevent construction
Singleton(const Singleton&); // Prevent construction by copying
Singleton& operator=(const Singleton&); // Prevent assignment
~Singleton(); // Prevent unwanted destruction
};
静态实例在您第一次调用Instance()
时创建,并在程序关闭时被销毁。
但要注意使用单身人士。它们并不是邪恶的,正如一些人认为的那样(我觉得这个位置不合理),但它们很容易被滥用并且难以正确使用。根据经验,不要将单例用于“接口类”(程序其他部分使用的接口类);尝试仅使用单例作为实现细节,并且只在感觉合适时使用。
修改 使用示例
前段时间我在gamedev.stackexchange上发布了一个答案,我提出的解决方案使用单例作为实现的一部分,而不是接口。代码进行了评论并解释了为什么需要单身人士:https://gamedev.stackexchange.com/a/17759/6188
答案 3 :(得分:8)
添加一个静态成员Singleton::DestroyInstance()
,删除该实例并从主要文件中调用它。
void Singleton::DestroyInstance() {
delete m_pInstance;
m_pInstance = 0;
}
/* ...................... */
int main()
{
Singleton* pInstance = Singleton::GetInstance();
/* ... */
Singleton::DestroyInstance();
}
答案 4 :(得分:7)
简短的回答,不要使用单身人士。
更长的回答,永远不要在main()
中的单例指针上调用delete。使用某种静态对象,在调用其他全局变量dtors时删除单例。
答案 5 :(得分:0)
使用Andrei Alexandrescu编写的Loki-Library的SingletonHolder。
#include "SingletonHolder"
class Singleton
{
//not allowed ctor
private:
Singleton()
{}
~Singleton()
{}
...
//Singelton on heap
friend struct Loki::CreateUsingNew<Singleton>;
}
Singleton& get_singleton_pointer()
{
return Loki::SingltonHolder<Singleton>::Instance();
}
在此示例中,将在程序结束时删除Singlton。 还有一些其他的stategie使用malloc创建单例指针,静态... 更多细节看看: http://loki-lib.sourceforge.net/html/a00628.html
Alternativ你可以使用静态变量创建单例:
template <typename T>
struct CreateUsingStatic
{
static T& get_T_singleton()
{
static T t;
return t;
}
};
class Singleton
{
...
private:
friend struct CreateUsingStatic<Singleton>;
}