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

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

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

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


当前回答

#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

例子:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);

其他回答

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++和双重检查锁定的危险:第一部分

除了这里的其他讨论之外,可能值得注意的是,您可以使用全局性,而不限于一个实例。例如,考虑引用计数的情况。。。

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

现在,在函数(如main)中的某个位置,您可以执行以下操作:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

ref不需要将指针存储回各自的store,因为这些信息是在编译时提供的。您也不必担心Store的生存期,因为编译器要求它是全局的。如果确实只有一个Store实例,那么这种方法没有开销;在不止一个实例的情况下,编译器需要对代码生成进行巧妙处理。如果需要,ItemRef类甚至可以成为Store的朋友(您可以有模板朋友!)。

如果Store本身是一个模板化的类,那么事情就变得更糟了,但是仍然可以使用这个方法,也许可以通过实现具有以下签名的helper类:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

用户现在可以为每个全局Store实例创建StoreWrapper类型(和全局实例),并始终通过其包装器实例访问商店(从而忘记使用Store所需的模板参数的详细信息)。

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

singleton<C>()

使用

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

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

它确实可能是从堆中分配的,但没有源就无从得知。

典型的实现(取自我在emacs中已有的一些代码)是:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

……然后依靠超出范围的程序进行清理。

如果您在必须手动进行清理的平台上工作,我可能会添加一个手动清理例程。

这样做的另一个问题是它不是线程安全的。在多线程环境中,两个线程可以通过“if”,然后任何一个线程都有机会分配新实例(所以两者都会)。如果你依靠程序终止来清理,这仍然不是什么大问题。

这里是一个使用CRTP的可模拟单例。它依赖于一个小助手在任何时候(最多)强制执行一个对象。要在程序执行过程中强制执行单个对象,请删除重置(我们发现这对测试很有用)。

ConcreteSinleton可以这样实现:

class ConcreteSingleton : public Singleton<ConcreteSingleton>
{
public:
  ConcreteSingleton(const Singleton<ConcreteSingleton>::PrivatePass&)
      : Singleton<StandardPaths>::Singleton{pass}
  {}
  
  // ... concrete interface
  int f() const {return 42;}

};

然后与一起使用

ConcreteSingleton::instance().f();