Howard
Howard

Reputation: 217

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

class Singleton {
public:
    static Singleton *getInstance() {
        return &singleton;
    }
private:
    Singleton();
    ~Singleton();
private:
    static Singleton singleton;
};

Someone says, this will cause some nasty bugs. But what bugs will this cause? Not using a pointer avoids double-checked-locking-pattern nicely.

Upvotes: 1

Views: 95

Answers (1)

Curious
Curious

Reputation: 21540

Bugs like this

SomethingElse.cpp

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

C++ does not specify the order of construction across two translation units (in this case that roughly equates to the different implementation files, one with your singleton and one with mine)

Now if the runtime decides to destroy your singleton before mine (i.e. construct it after mine), then my singleton will try and use yours in its destructor and constructor. And then undefined behavior follows.


Now if you had not made it a static global in your translation unit, and had used a function scoped static (which can use DCLP), then the C++ runtime would construct your singleton before mine and destroy your singleton after mine, and that is the nice order of execution 🙂


Also note that in some implementations, the compiler is free to not put in code for DCLP if you do not link with -pthread, so you might not get the costs you are worried about anyway.

Also note that what really causes slowdowns with threaded code is contention, if you don't have contention then most implementations of mutexes use what is called a futex, which enables no contention situations to avoid syscalls as much as possible. So if that was on your mind then that should not be much of a problem either.

But if you are still worried that static locals are a problem, then just be careful with whatever solution you use and make sure it doesn't have the potential to blow up badly. Basically the point I am trying to make is, trust the compiler


However if you still want to avoid function scoped statics, then use code like this

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

Note that I have not used std::make_unique here, for this interesting reason.

Also note that if you construct a unique_ptr globally like this

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

It is a part of static initialization and not dynamic initialization. And is therefore not prone to UB. It is guaranteed to happen before any singleton (that uses the singleton_instance ptr) will be constructed.


Also take a look at the static initialization fiasco

Upvotes: 2

Related Questions