The Singleton Pattern and a Promise You Can't Take Back
Why forcing a class to only ever have one instance is a bet on the future you can't undo, and the private-constructor trick that actually enforces it.
Here's the idea in one sentence: a singleton doesn't just keep you to one instance, it makes a private constructor guarantee that nobody, anywhere in the program, can ever create a second one.
Why "Only One" Is a Dangerous Promise
Picture a chat app. It feels natural to make the chat room a singleton: one instance, reachable from anywhere, so any part of the app can send and receive messages through it.
Then the app grows. Now you want multiple chat rooms. But the chat room was built as a singleton, built with the explicit guarantee that a second instance could never exist. That guarantee, made back when "just one" felt obviously true, is now the exact thing blocking the feature.
That's the real problem with this pattern. It isn't that having one instance is bad, plenty of things in a real app genuinely only need one. It's that forcing it, making a second instance impossible, is a bet that you will never need one, forever. And that's a bet almost nobody can honestly make.
There's a second cost too: testing. A singleton always hands back the exact same instance, but a test often wants a fake or a mock in its place. A global instance that can't be swapped out makes that far harder than it needs to be.
A Constructor Nobody Else Can Call
Here's the actual mechanism, and it's a genuinely clever trick even if you decide never to use it: make the constructor private.
class Singleton {
public:
static Singleton* getInstance() {
if (instance_ == nullptr) {
instance_ = new Singleton();
}
return instance_;
}
private:
Singleton() = default;
static Singleton* instance_;
};
Singleton* Singleton::instance_ = nullptr;A private constructor means new Singleton() is illegal everywhere outside the class itself. So how does anything ever get one? A static method, getInstance(), lives inside the class, which means it's allowed to call the private constructor. That's the entire trick: the only door in is a door the class controls itself.
The first time getInstance() runs, instance_ is empty, so it builds one and remembers it. Every call after that finds instance_ already set, and just hands back the same one.
Wiring It
Singleton* a = Singleton::getInstance();
Singleton* b = Singleton::getInstance();
std::cout << (a == b) << "\n"; // 1, same instancea and b are the exact same object, no matter how many times or how many places getInstance() gets called. Every caller shares one instance, because there's only ever one place that instance could have come from.
ExpandSingleton with a private constructor, a static getInstance() method, and a static instance variable, where the first caller builds it and every later caller gets the same one back
The next post looks at a real bug hiding in this exact code, one that only shows up the moment more than one thread calls getInstance() at once.
The Essentials
- A singleton makes a private constructor, so the only way to get an instance is through a class-controlled static method.
- Forcing "only ever one instance" is a bet that requirements never change. Plenty of things genuinely need one instance in practice, that's different from making a second one impossible forever.
Keep reading