【问题标题】:Lazy initialization of a C++ singleton in declaration or in implementation在声明或实现中延迟初始化 C++ 单例
【发布时间】:2017-07-12 09:25:25
【问题描述】:

我知道单例模式通常被认为是一种糟糕的设计,因此不鼓励使用,但这个问题涉及实现方面,而不是单例模式的适当性。

考虑以下三种使用延迟初始化的 C++ 单例实现:

1:使用指针,分离声明和实现

单例.hpp:

class Singleton {
public:
   static Singleton* instance();
private:
   Singleton() {}
   static Singleton* singleton;
};

单例.cpp:

Singleton* Singleton::singleton = nullptr;

Singleton* Singleton::instance() {
    if( nullptr == singleton ) {
        singleton = new Singleton();
    }
    return singleton;
}

2:使用引用,拆分声明和实现

单例.hpp:

class Singleton {
public:
   static Singleton& instance();
private:
   Singleton() {}
};

单例.cpp:

Singleton& Singleton::instance() {
    static Singleton singleton;
    return singleton;
}

3:使用引用,在声明中内联

单例.hpp:

class Singleton {
public:
    static Singleton& instance() {
         static Singleton singleton;
         return singleton;
    }
private:
    Singleton() {}
}

我个人喜欢并使用第三个版本。但是有什么好的理由更喜欢第一个或第二个版本吗?

据我了解,在第三个版本中,每个翻译单元都有一个包含Singleton.hpp 的对象实例,然后链接器选择一个。这会导致任何副作用吗?

在共享库中使用第三个有什么副作用吗?

额外问题:哪个实现实际上是“Meyer 的单例”?

【问题讨论】:

  • 井引用不能为空,所以不能使用第二种和第三种方法进行惰性实例化
  • @spug 带有初始化的局部静态变量将在第一次调用函数时被初始化。
  • 第二个选项(Meyers Singleton)保证在 C++11 中是线程安全的,你知道吗。
  • @StoryTeller 第二个版本也应该是线程安全的。它们之间的唯一区别是,在第二种选择中,instance 函数不能被内联。实例的初始化仍然是线程安全的。
  • @VTT - “没有显式控制”,这就是懒惰评估的重点......

标签: c++ design-patterns singleton


【解决方案1】:

第一个是线程安全的。

 if( nullptr == singleton ) {
    singleton = new Singleton();
}

多个线程可能会执行分配语句并造成内存泄漏

第二个和第三个是线程安全的自C++11,因为:

如果多个线程尝试初始化相同的静态本地 变量并发,初始化只发生一次(类似 可以使用 std::call_once 获得任意函数的行为。

来自here

我更喜欢第三个,因为内联优化更有可能。

【讨论】:

  • 定义“未定义行为”是否正确?对我来说,这只是一个普通的线程不安全代码。
  • @Wizard79 你可能是对的,我已经编辑了答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
相关资源
最近更新 更多