Question
In C++, I came across this implementation of the Singleton design pattern:
class Singleton
{
public:
static Singleton* getInstance();
~Singleton();
private:
Singleton();
static Singleton* instance;
};
From this declaration, it looks like instance may be created on the heap, which implies dynamic memory allocation. I am unsure when that memory would be released.
- Is the allocated memory ever deallocated automatically?
- Does this implementation risk causing a memory leak?
- If this approach is problematic, what is the correct way to implement a Singleton in C++?
I would like to understand both the memory-management issue and the recommended modern C++ approach.
Short Answer
By the end of this page, you will understand what the Singleton pattern is, why older heap-based implementations are often unsafe or unnecessary, and how to implement a Singleton correctly in modern C++. You will also learn about object lifetime, destruction, thread safety, and when Singleton should be avoided altogether.
Concept
The Singleton design pattern ensures that a class has only one instance and provides a global access point to that instance.
In older C++ code, Singleton was often implemented using a static pointer and new, like this:
Singleton* Singleton::instance = nullptr;
Singleton* Singleton::getInstance() {
if (!instance) {
instance = new Singleton();
}
return instance;
}
This raises an important question: who deletes the object?
If no code ever calls delete instance;, then the program leaks memory. In some older programs, developers accepted this because the operating system reclaims process memory when the program exits. But that is still poor design:
- the destructor may never run
- cleanup logic may be skipped
- ownership is unclear
- thread safety is not guaranteed
Why this matters
In real programs, object lifetime matters. A singleton may manage:
- configuration
- logging
- database access
- caches
- shared services
If its lifetime is not handled correctly, you can get:
- memory leaks
- shutdown-order bugs
- race conditions
- hard-to-test code
Mental Model
Think of a Singleton like a single shared office printer in a company.
- There should only be one printer.
- Everyone who needs it uses the same one.
- You do not want each employee creating a new printer.
Now imagine two ways to manage it:
- Heap-based version: someone buys a printer and puts the receipt in a drawer, but nobody knows who is responsible for throwing it away later.
- Local static version: the building automatically installs one printer the first time anyone needs it, and the building management removes it when the office closes.
The second model is safer because responsibility is clear.
That is what a function-local static does in C++: it creates the object once, stores it safely, and destroys it automatically at the end of the program.
Syntax and Examples
Preferred syntax in modern C++
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void sayHello() {
std::cout << "Hello from the singleton\n";
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
~Singleton() = default;
};
Usage:
#include <iostream>
int main() {
Singleton& s = Singleton::getInstance();
s.sayHello();
}
Why return a reference instead of a pointer?
Returning a reference:
Step by Step Execution
Consider this code:
#include <iostream>
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void printAddress() {
std::cout << this << '\n';
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() {
std::cout << "Constructor called\n";
}
};
int main() {
Singleton& a = Singleton::getInstance();
Singleton& b = Singleton::getInstance();
a.printAddress();
b.printAddress();
}
What happens step by step
main()starts.Singleton::getInstance()is called for .
Real World Use Cases
Singletons are used when an application truly needs one shared instance.
Common examples
- Logger: one central logging service used across the app
- Configuration manager: one shared source of app settings
- Application state: one global object representing runtime state
- Resource registry: one place to access registered services
- Hardware or OS wrapper: one interface to a unique system resource
Example scenarios
Logging
A desktop app may use one logger so all modules write messages consistently.
Configuration
A server may load settings once at startup and let the rest of the code read them from one shared object.
Metrics collection
A backend service may keep one metrics collector that records counters and timings.
When not to use Singleton
Singleton is often overused. Avoid it when:
- you can pass an object as a parameter instead
- you want easy unit testing
- different parts of the program may need different instances
- hidden global state would make the code harder to understand
In many codebases, dependency injection or regular object ownership is a better choice.
Real Codebase Usage
In real projects, developers often use the idea behind Singleton more carefully than textbook examples suggest.
Common patterns
1. Local static instance
This is the most common modern C++ pattern:
static Service& getInstance() {
static Service instance;
return instance;
}
Useful when one instance is truly required and global access is acceptable.
2. Deleted copy operations
To preserve the singleton guarantee, copying is disabled:
Service(const Service&) = delete;
Service& operator=(const Service&) = delete;
Sometimes move operations are deleted too.
3. Guarded initialization logic
A singleton may validate setup before use:
void setConfigPath(const std::string& path) {
if (path.empty()) {
throw std::invalid_argument("path cannot be empty");
}
configPath = path;
}
Common Mistakes
1. Allocating with new and never deleting
Broken example:
class Singleton {
public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
private:
static Singleton* instance;
};
Problem:
instanceis never deleted- destructor may never run
Better:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
2. Forgetting to disable copying
Broken example:
class Singleton {
public:
static Singleton& {
Singleton instance;
instance;
}
:
() = ;
};
{
Singleton a = Singleton::();
}
Comparisons
Singleton implementation approaches
| Approach | Memory management | Thread safety | Complexity | Recommended |
|---|---|---|---|---|
Raw pointer + new | Manual | Usually unsafe unless synchronized | Higher | No |
| Static local object | Automatic | Safe since C++11 | Low | Yes |
| Global object | Automatic | Depends on usage | Low | Sometimes |
std::unique_ptr singleton | Automatic but managed manually | Depends on implementation | Medium | Rarely needed |
Cheat Sheet
Modern C++ Singleton template
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() = default;
~Singleton() = default;
};
Rules
- Make the constructor private.
- Provide one access point, usually
getInstance(). - Return a reference when possible.
- Delete copy constructor and copy assignment.
- Prefer function-local
staticover raw pointer +new. - Since C++11, local static initialization is thread-safe.
Avoid
instance = new Singleton();
unless you have a very specific lifetime-management reason.
Good signs
FAQ
Does a singleton created with new leak memory in C++?
Yes, if nothing calls delete on it. The operating system may reclaim memory at process exit, but the object's destructor will not run automatically.
What is the best way to implement a Singleton in modern C++?
Use a function-local static object and return it by reference.
Is a local static singleton thread-safe?
Yes, initialization of function-local statics is thread-safe in C++11 and later.
Why should I delete the copy constructor in a singleton?
Because copying would allow more than one instance, which breaks the singleton rule.
Should getInstance() return a pointer or a reference?
A reference is usually better because it expresses that the object exists and is not nullable.
Is Singleton considered bad practice?
Not always, but it is often overused. It introduces global state, which can hurt testability and design clarity.
Can I use std::unique_ptr for a singleton?
You can, but it is usually unnecessary if a function-local static object already solves the lifetime problem cleanly.
Mini Project
Description
Build a simple application logger as a singleton. This project demonstrates how one shared object can be accessed from different parts of a program without creating multiple logger instances. It also reinforces safe singleton construction using a local static object.
Goal
Create a Logger singleton that prints messages and proves that every call uses the same instance.
Requirements
- Create a
Loggerclass with a private constructor. - Add a
getInstance()method that returns the single logger instance. - Prevent copying of the
Loggerobject. - Add a
log()method that prints a message. - In
main(), call the logger multiple times and show that the same instance is reused.
Keep learning
Related questions
Basic Rules and Idioms for Operator Overloading in C++
Learn the core rules, syntax, and common idioms for operator overloading in C++, including member vs non-member operators.
C++ Base Class Constructor Rules Explained
Learn how C++ base class constructors are called from derived classes, including order, syntax, defaults, and common mistakes.
C++ Casts Explained: C-Style Cast vs static_cast vs dynamic_cast
Learn the difference between C-style casts, static_cast, and dynamic_cast in C++ with clear examples, safety rules, and real usage tips.