最近,我遇到了C++的Singleton设计模式的实现/实现。它看起来是这样的(我采用了现实生活中的例子):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

从这个声明中,我可以推断实例字段是在堆上启动的。这意味着存在内存分配。对我来说完全不清楚的是,内存将在何时被释放?或者是否存在错误和内存泄漏?似乎在实现方面存在问题。

我的主要问题是,如何以正确的方式实现它?


当前回答

接受答案中的解决方案有一个明显的缺点——在控件离开main()函数后调用单例的析构函数。当一些依赖对象被分配到main中时,可能真的会有问题。

我在尝试在Qt应用程序中引入Singleton时遇到了这个问题。我决定,我的所有设置对话框都必须是Singleton,并采用了上面的模式。不幸的是,Qt的主类QApplication被分配在主函数的堆栈上,当没有应用程序对象可用时,Qt禁止创建/销毁对话框。

这就是为什么我更喜欢堆分配的单件。我为所有单例提供了显式的init()和term()方法,并在main内部调用它们。因此,我可以完全控制单体创建/销毁的顺序,而且我保证无论是否有人调用getInstance(),都会创建单体。

其他回答

2008年,我提供了一个Singleton设计模式的C++98实现,该模式是惰性评估的、保证破坏的,在技术上不是线程安全的:有人能给我提供一个c++中的Singleton示例吗?

这里是Singleton设计模式的一个更新的C++11实现,它是延迟评估的、正确销毁的和线程安全的。

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are inaccessible(especially from outside), otherwise, you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

请参阅本文,了解何时使用单例:(不经常)辛格尔顿:应该如何使用

请参阅这两篇关于初始化顺序和如何处理的文章:静态变量初始化顺序查找C++静态初始化顺序问题

请参阅这篇描述寿命的文章:C++函数中静态变量的生存期是多少?

请参阅本文,其中讨论了单线程的一些线程含义:Singleton实例声明为GetInstance方法的静态变量,它是线程安全的吗?

请参阅这篇文章,解释为什么双重检查锁定在C++上不起作用:C++程序员应该知道哪些常见的未定义行为?Dobbs博士:C++和双重检查锁定的危险:第一部分

有人提到过std::call_once和std::once_flag吗?大多数其他方法——包括双重检查锁定——都是失败的。

单例模式实现中的一个主要问题是安全初始化。唯一安全的方法是用同步屏障保护初始化序列。但这些障碍本身需要安全启动。std::once_flag是保证安全初始化的机制。

另一个非分配的选择:根据需要创建一个单例,比如C类:

singleton<C>()

使用

template <class X>
X& singleton()
{
    static X x;
    return x;
}

在当前C++中,这一点和Cætælin的答案都不是自动线程安全的,而是在C++0x中。

我们最近在EECS课上讨论了这个话题。如果您想详细查看课堂讲稿,请访问http://umich.edu/~eecs381/讲座/习语DesPattsCreational.pdf。这些笔记(以及我在这个答案中给出的引文)由我的教授大卫·基拉斯创建。

我知道有两种方法可以正确创建Singleton类。

第一种方式:

按照示例中的方式实现它。至于破坏,“单线程通常会持续程序运行的时间;当程序终止时,大多数操作系统都会恢复内存和大多数其他资源,因此有理由不用担心这一点。”

然而,在程序终止时进行清理是一种很好的做法。因此,您可以使用辅助静态SingletonDestructor类来完成此操作,并将其声明为Singleton中的朋友。

class Singleton {
public:
  static Singleton* get_instance();
  
  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

// somewhere in code (Singleton.cpp is probably the best place) 
// create a global static Singleton_destroyer object
Singleton_destoyer the_destroyer;

Singleton_destroyer将在程序启动时创建,“当程序终止时,所有全局/静态对象都会被运行库关闭代码(由链接器插入)破坏,因此_destroyer将被破坏;其析构函数将删除Singleton,运行其析构器。”

第二条路

这叫做Meyers Singleton,由C++向导Scott Meyers创建。只需以不同的方式定义get_instance()。现在还可以去掉指针成员变量。

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

这很简单,因为返回的值是通过引用的,您可以使用。语法而不是->来访问成员变量。

“编译器通过声明,而不是之后,然后在程序中删除静态对象结束"

还需要注意的是,使用Meyers Singleton时,如果对象在终止-相对于其他对象,Singleton何时消失?但对于简单的应用程序,这很好。"

以下是我对如何实现正确的单线图(以及其他非平凡静态对象)的看法:https://github.com/alex4747-pub/proper_singleton

摘要:

使用静态初始化列表在正确的时间实例化单线程:在进入main之后和启用多线程之前添加一些小的改进,使其对单元测试友好。