C++ 单身人士

C++ 单身人士,c++,design-patterns,singleton,C++,Design Patterns,Singleton,我有一个singleton类,其实例在类的CPP文件中的全局范围内初始化: Singleton* Singleton::uniqueInstance = new Singleton(); 其头文件如下所示: class Singleton { public: static Singleton& getInstance() { return *uniqueInstance; } static bool destroyInstance() { delete uniqueIn

我有一个singleton类,其实例在类的CPP文件中的全局范围内初始化:

Singleton* Singleton::uniqueInstance = new Singleton();
其头文件如下所示:

class Singleton {
public:
    static Singleton& getInstance() { return *uniqueInstance; }
    static bool destroyInstance() { delete uniqueInstance; }

private:
    //...
    //... typical singleton stuff
    static Singleton* uniqueInstance;
}; // end of class Singleton

我注意到它的析构函数dodn在程序终止期间不会执行,因此我添加了一个公共静态接口
Singleton::destrobutInstance()
,以便在程序退出之前由客户端代码手动调用,例如删除。此代码段不是完整的代码,并且假定还有其他代码处理线程安全问题。在这种情况下,如何利用RAII消除引入此类接口的需要?谢谢您的建议。

不会自动调用析构函数,因为全局对象是指针,而不是对象本身。如果要像这样声明全局对象:

Singleton Singleton::uniqueInstance();
然后会自动调用析构函数。但是,您无法准确控制何时调用构造函数和析构函数(与其他全局对象的构造和销毁相关)。如果这对您不重要,那么您可以使用上述方法。

单例最常见(通常也是最好)的变体是“泄漏单例”--即不尝试清理实例的变体。除非在非常不寻常的情况下,否则这并不是一个真正的问题,因为只有一个实例被泄漏,而且所有其他的事情都在执行,该实例无论如何都需要存在


如果您真的觉得有义务清理实例,一种工作相当好的方法(尽管它也有一些问题)是使用
atexit()
注册一个销毁实例的函数。

既然您打算使用RAII,我想您希望在退出本地作用域时销毁单例。如果是这样的话,我想知道你为什么要使用单例。根据定义,单例应该是全局的,因此在整个执行过程中都是活动的。也许基于实例的常规方法更适合您。

对于单例模式请确保您这样定义私有构造函数,以防止客户端创建许多对象:

class Singleton {
public:
    static Singleton& getInstance() { 

      if(!uniqueInstance)
         uniqueInstance = new Singleton();

      return *uniqueInstance; 
    }

    static bool destroyInstance() { delete uniqueInstance; }

private:
    Singleton() { }; //  
    static Singleton* uniqueInstance;
};

Singleton* Singleton::uniqueInstance(NULL);

除非您处于多线程环境中,否则单例很容易创建。如果可以,您将希望确保只有一个线程正在尝试创建您的单例(并因此销毁它)。一旦创建,它可以被多个线程同时使用。。。尽管这可能不是最好的解决办法

无论如何,一个非常简单的选择是:

class MySingleton
{
public:
  static MySingleton& Intance() { static MySingleton M_Instance; return M_Instance; }
private:
  MySingleton() {}
};
只要您不使用MI,并且在全局销毁期间不使用单例,它就可以正常工作。此外,它至少不适用于VC2003(用于为调用该方法的每个库实例化一个单例…),我不知道是否适用于更新的版本,我们已经完全停止在windows上编译了一段时间

现在,如果您真的想了解更多关于Singleton的信息:

  • 实例和销毁问题
  • 寿命变化
  • 穿线安全
Alexandrescu在中花了整整一章的时间来关注这一点,如果你无法阅读这本书,你仍然可以阅读由中的反射产生的代码


此外,您可以简单地使用其他方法进行设计。单例可能会使测试变得困难,支持依赖注入的人对此有一些有趣的想法。看看。

您有两种解决方案:

您的对象可以在“main”之前构造 。。。然后将其设置为全局对象:

// header
class Singleton {
public:
   static Singleton& getInstance() ;
   // ...

private:
    //...
}; // end of class

正如你提到的“在全局范围内初始化”,我想这就是你想要的

对象必须在“main”之后构造 。。。然后将其放入智能指针中:

// header
class Singleton {
public:
    static Singleton& getInstance()
    {
        if(uniqueInstance.get() == 0)
        {
           uniqueInstance.reset(new Singleton()) ;
        }

        return *uniqueInstance;
    }

    private:
    //...
    //... typical singleton stuff
    static std::auto_ptr<Singleton> uniqueInstance;
}; // end of class Singleton
//头
单件阶级{
公众:
静态单例&getInstance()
{
if(uniqueInstance.get()==0)
{
重置(新的Singleton());
}
返回*唯一实例;
}
私人:
//...
//…典型的单身生活
静态std::auto_ptr uniqueInstance;
}; // 下课单身汉

//源代码
std::auto_ptr Singleton::uniqueInstance;
因此,实例将在第一次使用时创建,然后在执行结束时由智能指针的析构函数销毁


不过,在创建时仍然存在多线程访问问题。这就是为什么我更喜欢第一种解决方案。

换一种方式。。。我正在尝试修复现有的单例代码。老实说,我不太确定RAII是否能帮助解决上述问题。需要刷新并关闭写入析构函数的文件:(atexit()…读到phoenix singleton时,我想起了一个乏味的问题:PHi Greg,我不太理解代码。你的意思是在singleton模式上使用常规全局对象吗?是的,如果你想自动调用对象析构函数,那么你必须使用常规全局对象。此外,singleton“模式”不是一个真正的模式,它只是一个如何编写模式的示例。尽管Greg发表了评论,但我认为他在最初的回答中并没有提到常规全局对象。您仍然可以使用此单例模式,但只需从使用指针改为使用类的常规实例。您忘记将实例声明为静态对象mberoops,愚蠢的我:x猜这就是为什么我们应该尝试编译示例:)将实例声明为本地静态对象在MinGW上工作。:)我没有带VC2003,所以无法对它进行测试。这被称为“Meyers Singleton”,在C++11中(以及在C++11之前的GCC中),它实际上是线程安全的。
// header
class Singleton {
public:
    static Singleton& getInstance()
    {
        if(uniqueInstance.get() == 0)
        {
           uniqueInstance.reset(new Singleton()) ;
        }

        return *uniqueInstance;
    }

    private:
    //...
    //... typical singleton stuff
    static std::auto_ptr<Singleton> uniqueInstance;
}; // end of class Singleton
// source
std::auto_ptr<Singleton> Singleton::uniqueInstance ;