27

编辑:抱歉我的问题不清楚,为什么书籍/文章更喜欢实现#1 而不是实现#2?

在 Singleton 类的实现中使用指针与使用静态对象的实际优势是什么?为什么大多数书都喜欢这个

class Singleton
{
  private:

    static Singleton *p_inst;
    Singleton();

  public:

    static Singleton * instance()
    {
      if (!p_inst)
      {
        p_inst = new Singleton();
      }

      return p_inst;
    }
};

超过这个

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
};
4

7 回答 7

19

为什么书籍/文章更喜欢实现#1 而不是实现#2?

因为大多数描述 Singleton 反模式的文章在尝试在 C++ 中安全地实现它时并没有完全理解所有的隐患。很难做到正确。

在 Singleton 类的实现中使用指针与使用静态对象的实际优势是什么?

使用带new但不带的指针delete可确保对象永远不会被销毁,因此在其生命周期结束后访问它就没有危险。结合“惰性”创建,第一次访问它,这保证了所有访问都是对有效对象的。缺点是创建不是线程安全的,并且对象和它获取的任何资源都不会在程序结束时释放。

使用本地静态对象,在任何支持 C++11 线程模型的编译器上创建都是线程安全的;此外,该对象将在程序结束时被销毁。但是,可以在对象销毁后访问该对象(例如,从另一个静态对象的析构函数中),这可能会导致严重的错误。

最好的选择是尽可能避免静态数据和全局可访问的数据。特别是,永远不要使用 Singleton 反模式;它将全局静态数据与奇怪的实例化限制相结合,使测试变得不必要地困难。

于 2012-10-24T11:04:33.620 回答
9

第二个版本(使用局部静态变量)具有显着优势。

它不需要使用自由存储,因此不会被检测为内存泄漏。它是线程安全的(in C++11)。它更短更简单。

唯一的缺点是不可能使其成为可移植的线程安全的(对于 C++11 之前的编译器),并且它没有为您提供显式销毁单例实例的选项。

于 2012-10-24T10:42:40.130 回答
4

总是更喜欢第二种,但第一种确实有一些潜在的有趣优势:-

  • 清晰 - 检查指针是否为空实际上是编译器在构造静态对象时所做的工作。从“学习”的角度来看,了解在方法范围内使用静态对象时发生的情况是有启发性的。

  • 惰性分配 - 在第一种情况下,Singleton 对象是堆分配的。如果您的函数永远不会运行,则永远不会构造对象并且永远不会消耗内存。但是,在第二种情况下,链接器在程序启动之前分配内存来保存对象,即使“构造”是惰性的。

于 2012-10-24T10:55:14.187 回答
3

第二个具有非确定性破坏。第一个,您可以控制何时删除指针(如果有的话)。

当然,第一个构造不是线程安全的,但可以使用boost::call_once(或std::call_once如果可用)来实现

第二个构造很常见,以至于许多编译器使它成为线程安全的,即使从技术上来说它不是标准的(尽管按照标准,对象应该只创建一次,我不确定标准对完成的看法在另一个线程使用它之前进行构造)。

如果销毁顺序没有问题,那么您可以继续使用静态版本,只要您的编译器保证它是线程安全的。

于 2012-10-24T10:43:27.140 回答
3

第二个例子被称为“Meyers' Singleton”,因为它首先发表在“Effective C++”或“More Effective C++”中。我不确定是哪一个,但两者都是在“设计模式”之后出版的——所以四人组在他们的书写作时可能还没有意识到第二种模式。

此外,第一种方法对于其他语言来说更为标准——你可以用 Java 或 C# 做第一种,但不能用第二种方法,所以来自不同背景的人可能是第一种更出名的另一个原因。

在技​​术方面,第一种方法可以控制单例何时被销毁,但这也可能会给您带来很多麻烦。

于 2012-10-24T10:58:51.507 回答
1

一个优点是您不必检查单例是否已经被实例化。

另一个是您不必担心取消分配任何内存。

于 2012-10-24T10:42:43.500 回答
-3

非本地静态怎么样?有人看到这个问题吗?

class Singleton
{
    static Singleton singleton;
    Singleton();
    // etc

public:
    static Singleton &Instance() { return singleton; }
};

Singleton Singleton::singleton;

// etc
于 2014-04-03T10:35:19.180 回答