如果我这样写,我的单例类会导致什么错误

what bugs will my singleton class cause if I write it like this

本文关键字:什么 错误 单例类 我的 如果      更新时间:2023-10-16
class Singleton {
public:
    static Singleton *getInstance() {
        return &singleton;
    }
private:
    Singleton();
    ~Singleton();
private:
    static Singleton singleton;
};

有人说,这会导致一些讨厌的错误。但是这会导致什么错误?不使用指针可以很好地避免双重检查锁定模式。

这样的错误

别的什么.cpp

class SomethingSingleton {
public:
    SomethingSingleton() {
        auto* singleton = Singleton::getInstance();
        singleton->whatever();
    }
    ~SomethingSingleton() {
        auto* singleton = Singleton::getInstance();
        singleton->whatever();
    }
};
static SomethingSingleton something;

C++ 没有指定两个翻译单元的构造顺序(在这种情况下,大致相当于不同的实现文件,一个与你的单例,一个与我的

(

现在,如果运行时决定在我的之前销毁你的单例(即在我的之后构造它(,那么我的单例将尝试在其析构函数和构造函数中使用你的单例。 然后是未定义的行为


现在,如果你没有在翻译单元中将其设置为静态全局,并且使用了函数范围的静态(可以使用 DCLP(,那么C++运行时将在我之前构造你的单例,在我的之后销毁你的单例,这是一个很好的执行 顺序


另请注意,在某些实现中,如果您不与 -pthread 链接,编译器可以自由地不为 DCLP 输入代码,因此您无论如何都可能无法获得您担心的成本。

另请注意,真正导致线程代码速度变慢的是争用,如果您没有争用,那么互斥体的大多数实现都使用所谓的 futex,它允许没有争用情况以尽可能避免系统调用。 因此,如果您对此有所思虑,那么这也应该不是什么大问题。

但是,如果您仍然担心静态局部是一个问题,那么无论您使用什么解决方案,都要小心,并确保它不会严重爆炸。 基本上我想说的是,相信编译器


但是,如果您仍然想避免函数范围的静态,请使用这样的代码

static Something& get() {
    if (!something_instance) {
        something_instance = std::unique_ptr<Something>(new Something{});
    }
    return *something_instance;
}

请注意,由于这个有趣的原因,我在这里没有使用std::make_unique

另请注意,如果您像这样全局构造unique_ptr

std::unique_ptr<Singleton> Singleton::singleton_instance{};

它是静态初始化的一部分,而不是动态初始化的一部分。 因此不容易发生 UB。 它保证在任何单例(使用 singleton_instance ptr(被构造之前发生。


也看看静态初始化惨败