Skip to content

Commit fb8dc01

Browse files
committed
Update README.md
1 parent 89e4804 commit fb8dc01

1 file changed

Lines changed: 144 additions & 55 deletions

File tree

README.md

Lines changed: 144 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,169 @@
1-
# Singleton pattern examples in C++
1+
# Singleton Pattern Examples in C++
22

3-
A collection of minimal, self-contained C++ examples demonstrating multiple ways to implement the Singleton design pattern. The repository includes modern, thread-safe techniques (Meyer's Singleton) and legacy aproaches (Double-Checked Locking) for comparison.
3+
A collection of minimal, self-contained C++ examples demonstrating multiple ways to implement the Singleton design pattern. The repository includes modern, thread-safe techniques (Meyer's Singleton) and legacy approaches (Double-Checked Locking) for comparison.
44

55
## 🔍 Overview
66

7+
<table>
8+
<thead>
9+
<tr>
10+
<th>Example</th>
11+
<th>Initialization</th>
12+
<th>Storage duration</th>
13+
<th>Cleanup</th>
14+
<th>Thread-safe Initialization</th>
15+
<th>Notes</th>
16+
</tr>
17+
</thead>
18+
<tbody>
19+
<tr>
20+
<td><code>singleton-classic-example</code></td>
21+
<td>Eager</td>
22+
<td>Static</td>
23+
<td>Automatic</td>
24+
<td>Yes</td>
25+
<td>
26+
Can suffer from SIOF and static destruction order issues.
27+
</td>
28+
</tr>
29+
<tr>
30+
<td><code>singleton-meyers-example</code></td>
31+
<td>Lazy</td>
32+
<td>Static</td>
33+
<td>Automatic</td>
34+
<td>Yes (since C++11)</td>
35+
<td>
36+
Avoids classic SIOF because the object is created on first use.<br><br>
37+
May still suffer from static destruction order issues during program shutdown.
38+
</td>
39+
</tr>
40+
<tr>
41+
<td><code>singleton-classic-dynamic-example</code></td>
42+
<td>Lazy</td>
43+
<td>Dynamic</td>
44+
<td>Manual</td>
45+
<td>No</td>
46+
<td>
47+
Races may occur if multiple threads call <code>getInstance()</code> or
48+
<code>delInstance()</code> concurrently.<br><br>
49+
Manual lifetime management is error-prone.
50+
</td>
51+
</tr>
52+
<tr>
53+
<td><code>singleton-cherno-example</code></td>
54+
<td>Lazy</td>
55+
<td>Dynamic</td>
56+
<td>Manual</td>
57+
<td>No</td>
58+
<td>
59+
Similar problems to <code>singleton-classic-dynamic-example</code>: manual cleanup,
60+
unsafe lifetime management and no thread safety.
61+
</td>
62+
</tr>
63+
<tr>
64+
<td><code>singleton-dclp-example</code></td>
65+
<td>Lazy</td>
66+
<td>Dynamic</td>
67+
<td>Manual</td>
68+
<td>No</td>
69+
<td>
70+
Classic Double-Checked Locking Pattern (DCLP).<br><br>
71+
Historically unsafe because of data races and reordering issues.<br><br>
72+
Obsolete in modern C++.
73+
</td>
74+
</tr>
75+
<tr>
76+
<td><code>singleton-leaky-example</code></td>
77+
<td>Lazy</td>
78+
<td>Dynamic</td>
79+
<td>None</td>
80+
<td>Yes (since C++11)</td>
81+
<td>
82+
Intentionally leaks memory to avoid shutdown-order problems.<br><br>
83+
Useful when destruction order is more dangerous than not deleting the instance.
84+
</td>
85+
</tr>
86+
</tbody>
87+
</table>
88+
89+
---
90+
791
### 1️⃣ singleton-classic-example
892

9-
* 🔳 **Singleton with a static member instance.**
10-
* 🧩 **Static member variable**
11-
* 💾 **Static memory allocation**
12-
***Eager initialization** → Created before `main` starts
13-
* 🧼 **Automatic cleanup** → Destroyed after `main` exits
14-
* 🔒 **Initialization is Thread-safe**
15-
* The static member is initialized before `main` in a single-threaded context, so no construction race is possible.
16-
* ⚠️ **Static Initialization Order Fiasco**
17-
* Can suffer from SIOF If the singleton instance is accessed during the initialization of another static object, it may lead to UB due to the order of initialization.
18-
* ⚠️ **Static Destruction Order Fiasco**
19-
* A symmetric problem, If one static object's destructor calls another static that has been destroyed, it results in UB.
93+
🔳 **Singleton with a static member instance.**
94+
* 🧩 **Variable**: Static member variable
95+
* 💾 **Storage**: Static storage duration
96+
***Initialization:** Eager → Created before `main()` starts
97+
* 🧼 **Cleanup:** Automatic → Destroyed after `main()` exits
98+
* 🔒 **Thread safety:** Construction is effectively safe because initialization happens before `main()` in a single-threaded context
99+
* ⚠️ **Static Initialization Order Fiasco ([SIOF](https://en.cppreference.com/w/cpp/language/siof.html)):**
100+
* If a static object in another translation unit accesses the singleton during its own initialization, the singleton may not be constructed yet.
101+
* ⚠️ **Static Destruction Order Fiasco:**
102+
* If a static object in another translation unit accesses the singleton during its own destruction, the singleton may already have been destroyed.
20103

21104
### 2️⃣ singleton-meyers-example
22105

23-
* 🔳 **Meyer's Singleton** → The simplest and safest modern C++ singleton implementation.
24-
* 🧩 **Static local variable**
25-
* 💾 **Static memory allocation**
26-
***Lazy initialization** → Created only on first call to `getInstance()`
27-
* 🧼 **Automatic cleanup** → Destroyed after `main` exits
28-
* 🔒 **Initialization is Thread-safe since C++11**
106+
🔳 **Meyer's Singleton** → The simplest and safest modern C++ singleton implementation.
107+
* 🧩 **Variable**: Static local variable
108+
* 💾 **Storage**: Static storage duration
109+
***Initialization:** Lazy → Created only on first call to `getInstance()`
110+
* 🧼 **Cleanup:** Automatic → Destroyed after `main()` exits
111+
* 🔒 **Thread safety:** Initialization is thread-safe since C++11
29112
* A function-local static variable is initialized exactly once, even in a multi-threaded environment.
30-
* ⚠️ The Meyer's Singleton fixes **Static Initialization Order Fiasco**, but can suffer from **Static Destruction Order Fiasco**.
113+
***Fixes SIOF:** Avoids the classic static initialization order problem because the object is created on first use.
114+
* ⚠️ **Still vulnerable to Static Destruction Order Fiasco**
31115

32116
### 3️⃣ singleton-classic-dynamic-example
33-
* **Singleton with a static member pointer** → Dynamically allocated on first use.
34-
* 🧩 **Static member pointer**
35-
* 💾 **Dynamic memory allocation**
36-
***Lazy initialization** → Created only on first call to `getInstance()`
37-
* 🧹 **Manual cleanup** → Requires manual destruction via `delInstance()`
38-
* ⚠️ **Not Thread-safe**
117+
118+
🔳 **Singleton with a static member pointer** → Dynamically allocated on first use.
119+
* 🧩 **Variable**: Static member pointer
120+
* 💾 **Storage**: Dynamic storage duration
121+
***Initialization:** Lazy → Created only on first call to `getInstance()`
122+
* 🧹 **Cleanup:** Manual → Requires manual destruction via `delInstance()`
123+
* ⚠️ **Thread safety:** Not guaranteed
39124
* Two threads could call `delInstance()` simultaneously, or one calls `getInstance()` while another calls `delInstance()`, leading to a race on the pointer.
40-
* ⚠️ **Static Destruction Order Fiasco**
41-
* If another static's destructor calls `getInstance()` after `delInstance()` has run, you're dereferencing a deleted pointer, right back to UB.
42-
* ❗ Not recommended for multi-threaded applications.
125+
* ⚠️ **Manual lifetime management is error-prone**
126+
* Because the singleton is destroyed explicitly via `delInstance()`, the program must ensure no code still uses the old instance after deletion.
43127

44128

45129
### 4️⃣ singleton-cherno-example
46-
* **Cherno-style Singleton**[Why I don't like Singletons - Youtube](https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY)
47-
* 🧩 **Static global variable**
48-
* 💾 **Dynamic memory allocation**
49-
***Lazy initialization** → Created only on first call to `getInstance()`
50-
* 🧹 **Manual cleanup** → Requires manual destruction via `delInstance()`
51-
* ⚠️ **Not Thread-safe** → Same as [singleton-classic-dynamic-example](#3️⃣-singleton-classic-dynamic-example)
52-
* ❗ Not recommended for multi-threaded applications.
130+
131+
🔳 **Cherno-style Singleton** — inspired by [Why I don't like Singletons - Cherno](https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY)
132+
133+
* 🧩 **Variable**: Static global pointer
134+
* 💾 **Storage**: Dynamic storage duration
135+
***Initialization:** Lazy → Created only on first call to `getInstance()`
136+
* 🧹 **Cleanup:** Manual → Requires manual destruction via `delInstance()`
137+
* ⚠️ **Thread safety:** Not guaranteed, same as [singleton-classic-dynamic-example](#3️⃣-singleton-classic-dynamic-example)
138+
* ⚠️ **Manual lifetime management is error-prone** → Same as [singleton-classic-dynamic-example](#3️⃣-singleton-classic-dynamic-example)
53139

54140
### 5️⃣ singleton-dclp-example
55-
* **Singleton with Double-Checked Locking Pattern (DCLP)** → Classic but unsafe lazy-initialization pattern.
56-
* 🧩 **Static member pointer**
57-
* 💾 **Dynamic memory allocation**
58-
***Lazy initialization** → Created only on first call to `getInstance()`
59-
* 🧹 **Manual cleanup** → Requires manual destruction via `delInstance()`
60-
* ⚠️ **Not Thread-safe** → Suffers from data races and reordering issues.
141+
142+
🔳 **Singleton with Double-Checked Locking Pattern (DCLP)** → Classic, but unsafe lazy-initialization pattern.
143+
* 🧩 **Variable**: Static member pointer
144+
* 💾 **Storage**: Dynamic storage duration
145+
***Initialization:** Lazy → Created only on first call to `getInstance()`
146+
* 🧹 **Cleanup:** Manual → Requires manual destruction via `delInstance()`
147+
* ⚠️ **Thread safety:** Not guaranteed, suffers from data races and reordering issues.
61148
***DCLP is unreliable** → Multiple threads may observe a partially constructed object.
62-
***Obsoleted by C++11**Local static initialization is the correct modern solution.
63-
* Reference: [C++ and the Perils of Double-Checked Locking by Scott Meyers and Andrei Alexandrescu](https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
149+
***Largely obsolete since C++11**Thread-safe function-local static initialization is the simpler and preferred modern solution.
150+
* 📖 **Reference:** [C++ and the Perils of Double-Checked Locking by Scott Meyers and Andrei Alexandrescu](https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
64151

65152
### 6️⃣ singleton-leaky-example
66-
* 🔳 **"Leaky" Singleton** → A heap-allocated Meyer's Singleton.
67-
* 🧩 **Static local pointer**
68-
* 💾 **Dynamic memory allocation**
69-
***Lazy initialization** → Created only on first call to `getInstance()`
70-
* 🧹 **No cleanup** → You are intentionally leaking the memory.
71-
* When the process ends, the Operating System reclaims the entire memory block anyway. "Leaking" at process exit is technically harmless.
72-
* 🔒 **Initialization is Thread-safe since C++11**
73-
* The local static pointer initialization is still protected by C++11's thread-safe static init guarantee, so the `new` only fires once, safely.
74-
***Avoids Static Initialization/Destruction Order Fiasco**
75-
* Since the destructor is never called, it can't try to access other dead objects during shutdown.
76153

154+
🔳 **"Leaky" Singleton** → A heap-allocated singleton initialized through a function-local static pointer.
155+
* 🧩 **Variable**: Static local pointer
156+
* 💾 **Storage**: Dynamic storage duration
157+
***Initialization:** Lazy → Created only on first call to `getInstance()`
158+
* 🧹 **Cleanup:** No cleanup, the object is intentionally never deleted.
159+
* The memory is intentionally left allocated until process termination.
160+
* In practice, the operating system reclaims the memory when the process exits.
161+
* 🔒 **Thread safety:** Initialization is thread-safe since C++11
162+
* The local static pointer initialization is still protected by C++11's thread-safe static init guarantee, so the `new` only fires once, safely.
163+
***Avoids both construction and destruction order problems**
164+
* Because the object is created on first use, it avoids SIOF
165+
* Because it is never destroyed, it avoids static destruction order issues
166+
* ⚠️ **Trade-off:** intentionally leaks memory by design
77167
---
78168

79169
## ⚙️ Prerequisites
@@ -176,4 +266,3 @@ build/windows-ninja-debug/singleton-meyers-example/02-singleton-meyers-example.e
176266
```bash
177267
./build/linux-ninja-debug/singleton-meyers-example/02-singleton-meyers-example
178268
```
179-

0 commit comments

Comments
 (0)