【问题标题】:How to delete Singleton pointer?如何删除单例指针?
【发布时间】:2012-01-02 09:53:40
【问题描述】:

我正在实现一个单例模式。在这里,我正在 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;  
}     

【问题讨论】:

  • 我在一个单线程应用程序中
  • 为什么要销毁单例?静态分配。
  • 单例中的全部要点是您不能删除它。它应该代表一个始终可访问的对象。如果不是,那么它当然不应该是单例
  • @Atul 你为什么要定义NULL 0?语言已经提供了这一点。顺便说一句,如果你的编译器支持 C++11,你应该改用nullptr
  • @jalf "它应该代表一个始终可以访问的对象。如果不是,那么它肯定不应该是一个单例"......对不起,我请求推迟.这根本不是单例的“全部要点”。最多可能是副作用。顾名思义,单例的意义在于防止您的应用程序创建该类的多个对象

标签: c++ singleton


【解决方案1】:

当然会导致无限循环!

你调用了析构函数,但是析构函数也调用了析构函数,所以析构函数又调用了析构函数......又......

如果你想使用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.
       ...
   }

注意:其他人警告你不要使用单例,他们是对的,因为这种模式经常被误用。所以使用前请三思。但不管怎样,这是学习的好方法!

【讨论】:

  • "所以在使用它之前请三思而后行。不管怎样,这是学习的好方法!"这是很好的建议,找出人们反对某事的原因的最佳方法是无论如何都要去做并得出相同的结论。
【解决方案2】:

虽然在大多数情况下最好的做法是不使用单例模式,但最好在函数中使用静态局部变量来创建单例:

static Singleton& Singleton::GetInstance() {
     static Singleton the_singleton;
     return the_singleton; 
}

为最佳实践提供一些理由:Singleton-nage 通常是不必要的,除非您必须代表一个真正的全局资源。单例具有全局变量的所有缺点(因为它们是带有一些 OO 结冰的全局变量),并且通常没有理由成为真正的单数。天真的程序员可能希望将God 实现为单例对象。当客户变成多神论者时,聪明的程序员不会并且高兴。

【讨论】:

  • +1 表示唯一性问题。我经常看到人们将数据库访问器实现为单例,然后他们需要第二个数据库......
【解决方案3】:

这是一个更正确的单例实现:

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

【讨论】:

  • 如果一个工具没有正当或有价值的用途,它是“邪恶的”吗?也许不是,但你也不应该考虑将它放入你的代码中。随意证明我错了,并告诉我在哪里可以合法地使用单例解决问题比非单例解决方案更好
  • @jalf 我将仅举几个受人尊敬的库示例:Boost.Serialization 和 Boost.Python。
  • 这不是示例,这只是名称删除。这些库在哪里使用单例,为什么它是一个更好的解决方案?根据定义,Boost 的解决方案并不总是最好的。否则,每个解析器都将使用 Boost.Spirit 编写。 ;)
  • 全球,全球,访问,全球,访问,等等。没有一个理由证明奇点是正当的。如果你想证明单例的合理性,你需要证明整个包的合理性。
  • “滥用全局实例”是什么意思?至于另一点,您似乎是在说您喜欢单例的原因是您可以使用单例通常没有的属性来扩展它们,但是您可以添加到任何任意类型.在这种情况下,你不喜欢单例,而且你不是在为单例辩护。
【解决方案4】:

简短回答,不要使用单例。

更长的答案,永远不要在 main() 中的单例指针上调用 delete。当调用其他全局变量 dtors 时,使用某种静态对象将删除单例。

【讨论】:

  • 如果你能通过扩展给定的例子来解释它会很棒吗?
  • @wilx: 或使用atexit,它就是为此而生的。
  • 哇。什么答案,这个答案应该得到-2000或其他东西......为什么你的简短回答是“不要使用单身人士”?有些地方你需要使用“单例”(很少见,但仍然会发生),否则就不会发明设计模式......
  • @MuhamedCicak FYI 模式不是发明的。它们是通过观察现有代码库来发现的。 20 年前做出的决定在今天被证明是错误的情况并不罕见,而普及单例模式的 GoF 书籍是从 1994 年开始的。那本书的作者之一 FTR 现在说单例模式是一个错误。
【解决方案5】:

添加一个静态成员Singleton::DestroyInstance() 删除实例并从主调用它。

void Singleton::DestroyInstance() {
    delete m_pInstance;
    m_pInstance = 0;
}

/* ...................... */

int main()  
{
    Singleton* pInstance = Singleton::GetInstance();
    /* ... */
    Singleton::DestroyInstance();    
}  

【讨论】:

  • 不要这样做!!如果在执行过程中调用exit(),单例不会被销毁。
【解决方案6】:

使用由 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 将在程序结束时被删除。 还有其他一些策略可以使用 malloc、静态... 更详细的看看: http://loki-lib.sourceforge.net/html/a00628.html

您可以仅使用静态变量创建单例:

template <typename T>
struct CreateUsingStatic
{
   static T& get_T_singleton()
   {
      static T t;
      return t;
   }
};

class Singleton
{
   ...
private:
   friend struct CreateUsingStatic<Singleton>;
}

【讨论】:

    猜你喜欢
    • 2014-09-06
    • 1970-01-01
    • 2013-09-09
    • 2015-08-09
    • 2011-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-08
    相关资源
    最近更新 更多