From b0b46a8557b71c42ed4d160fbaf6abfa37834820 Mon Sep 17 00:00:00 2001 From: Ionut Tomos Date: Sat, 7 Mar 2026 22:10:49 +0200 Subject: [PATCH 1/8] Reworked classic example. --- .../CMakeLists.txt | 6 +++--- .../inc/singleton.h | 0 .../src/main.cpp | 5 +++-- .../src/singleton.cpp | 0 CMakeLists.txt | 2 +- README.md | 18 +++++++++--------- 6 files changed, 16 insertions(+), 15 deletions(-) rename {singleton-classic-static-example => 01-singleton-classic-example}/CMakeLists.txt (50%) rename {singleton-classic-static-example => 01-singleton-classic-example}/inc/singleton.h (100%) rename {singleton-classic-static-example => 01-singleton-classic-example}/src/main.cpp (50%) rename {singleton-classic-static-example => 01-singleton-classic-example}/src/singleton.cpp (100%) diff --git a/singleton-classic-static-example/CMakeLists.txt b/01-singleton-classic-example/CMakeLists.txt similarity index 50% rename from singleton-classic-static-example/CMakeLists.txt rename to 01-singleton-classic-example/CMakeLists.txt index 8d6a9db..c29089a 100644 --- a/singleton-classic-static-example/CMakeLists.txt +++ b/01-singleton-classic-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-classic-static-example VERSION 0.1.0 LANGUAGES CXX) +project(01-singleton-classic-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-classic-static-example +add_executable(01-singleton-classic-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-classic-static-example PRIVATE +target_include_directories(01-singleton-classic-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/singleton-classic-static-example/inc/singleton.h b/01-singleton-classic-example/inc/singleton.h similarity index 100% rename from singleton-classic-static-example/inc/singleton.h rename to 01-singleton-classic-example/inc/singleton.h diff --git a/singleton-classic-static-example/src/main.cpp b/01-singleton-classic-example/src/main.cpp similarity index 50% rename from singleton-classic-static-example/src/main.cpp rename to 01-singleton-classic-example/src/main.cpp index bcf4992..ca53552 100644 --- a/singleton-classic-static-example/src/main.cpp +++ b/01-singleton-classic-example/src/main.cpp @@ -2,11 +2,12 @@ #include /* - * Example with STATIC MEMBER VARIABLE + * Example with static member variable * Static memory allocation * Eager initialization * Singleton is created before main() and destroyed after main() call - * Thread-safety not guaranteed + * Initialization is thread-safe - The static member is initialized before main() in a single-threaded context, so no construction race is possible. + * The Static Initialization Order Fiasco - If the singleton instance is accessed during the initialization of another static object. */ int main() { diff --git a/singleton-classic-static-example/src/singleton.cpp b/01-singleton-classic-example/src/singleton.cpp similarity index 100% rename from singleton-classic-static-example/src/singleton.cpp rename to 01-singleton-classic-example/src/singleton.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ccf6896..f096e57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,9 @@ project(cpp-singleton-pattern-examples LANGUAGES CXX ) +add_subdirectory(01-singleton-classic-example) add_subdirectory(singleton-cherno-example) add_subdirectory(singleton-meyers-example) -add_subdirectory(singleton-classic-static-example) add_subdirectory(singleton-classic-dynamic-example) add_subdirectory(singleton-dclp-example) add_subdirectory(singleton-smart-pointer-example) \ No newline at end of file diff --git a/README.md b/README.md index e3e34c4..0f68230 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,15 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways ## πŸ” Overview +### 1️⃣ singleton-classic-example +* πŸ”³ Singleton with a **static member instance**. +* 🧩 Static member variable +* πŸ’Ύ Static memory allocation +* ⚑ Eager initialization (constructed before `main` starts) +* 🧼 Automatic destruction after `main` exits +* πŸ”’ Initialization is Thread-safe β†’ The static member is initialized before `main` in a single-threaded context, so no construction race is possible. +* ⚠️ Can suffer from the **Static Initialization Order Fiasco** β†’ If the singleton instance is accessed during the initialization of another static object, it may lead to undefined behavior due to the order of initialization. + ### ⭐ singleton-meyers-example * Meyers Singleton β€” the simplest and safest modern C++ singleton implementation. * 🧩 Static local variable @@ -23,15 +32,6 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * ⚠️ Not thread-safe * πŸ”΄ Suitable only for single-threaded -### ⭐ singleton-classic-static-example -* Singleton with a static member instance β€” created eagerly at program startup. -* 🧩 Static member variable -* πŸ’Ύ Static memory allocation -* ⚑ Eager initialization (constructed before main() starts) -* 🧼 Automatic destruction after main() exits -* ⚠️ Not thread-safe -* πŸ”΄ Can suffer from the static initialization order fiasco - ### ⭐ singleton-classic-dynamic-example * Singleton with a static member pointer β€” dynamically allocated on first use. * 🧩 Static member pointer From 40a0e73ba212c745704055293a38e68bd04201bf Mon Sep 17 00:00:00 2001 From: Ionut Tomos Date: Sat, 7 Mar 2026 23:02:07 +0200 Subject: [PATCH 2/8] Reworked Meyer's example. --- .clang-format | 62 +++++++++---------- 01-singleton-classic-example/inc/singleton.h | 2 +- 01-singleton-classic-example/src/main.cpp | 13 +++- .../src/singleton.cpp | 6 +- .../CMakeLists.txt | 6 +- .../inc/singleton.h | 2 +- .../src/main.cpp | 4 +- .../src/singleton.cpp | 6 +- CMakeLists.txt | 2 +- CMakePresets.json | 9 +-- README.md | 45 ++++++++++---- 11 files changed, 94 insertions(+), 63 deletions(-) rename {singleton-meyers-example => 02-singleton-meyers-example}/CMakeLists.txt (53%) rename {singleton-meyers-example => 02-singleton-meyers-example}/inc/singleton.h (96%) rename {singleton-meyers-example => 02-singleton-meyers-example}/src/main.cpp (85%) rename {singleton-meyers-example => 02-singleton-meyers-example}/src/singleton.cpp (50%) diff --git a/.clang-format b/.clang-format index 25ed77c..4ea37d6 100644 --- a/.clang-format +++ b/.clang-format @@ -1,12 +1,12 @@ --- -Language: Cpp +Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right -AlignOperands: true +AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false @@ -21,18 +21,18 @@ AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: true BraceWrapping: - AfterClass: false + AfterClass: false AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false + AfterStruct: false + AfterUnion: false AfterExternBlock: false - BeforeCatch: false - BeforeElse: true - IndentBraces: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true @@ -45,39 +45,39 @@ BreakConstructorInitializersBeforeComma: true BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 160 -CommentPragmas: '^ IWYU pragma:' +ColumnLimit: 160 +CommentPragmas: "^ IWYU pragma:" CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false -DisableFormat: false +DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH -IncludeBlocks: Preserve +IncludeBlocks: Preserve IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '(Test)?$' + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: ".*" + Priority: 1 +IncludeIsMainRegex: "(Test)?$" IndentCaseLabels: false IndentPPDirectives: None -IndentWidth: 4 +IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' +MacroBlockBegin: "" +MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCBinPackProtocolList: Auto @@ -93,8 +93,8 @@ PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left -ReflowComments: true -SortIncludes: true +ReflowComments: true +SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: false @@ -106,13 +106,13 @@ SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 -SpacesInAngles: false +SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 4 -UseTab: Never +Standard: Latest +TabWidth: 4 +UseTab: Never ... diff --git a/01-singleton-classic-example/inc/singleton.h b/01-singleton-classic-example/inc/singleton.h index e444e66..e0c43e0 100644 --- a/01-singleton-classic-example/inc/singleton.h +++ b/01-singleton-classic-example/inc/singleton.h @@ -10,7 +10,7 @@ class Singleton { return instance; } - void func(); + void info(); private: Singleton(); diff --git a/01-singleton-classic-example/src/main.cpp b/01-singleton-classic-example/src/main.cpp index ca53552..f7268fa 100644 --- a/01-singleton-classic-example/src/main.cpp +++ b/01-singleton-classic-example/src/main.cpp @@ -1,5 +1,7 @@ #include "singleton.h" #include +#include +#include /* * Example with static member variable @@ -13,7 +15,16 @@ int main() { std::cout << "--- main start ---" << std::endl; - Singleton::getInstance().func(); + std::vector threads; + + // Launch 10 threads + for (int i = 0; i < 10; ++i) { + threads.emplace_back([]() { Singleton::getInstance().info(); }); + } + + for (auto& t : threads) { + t.join(); + } std::cout << "--- main end ---" << std::endl; } \ No newline at end of file diff --git a/01-singleton-classic-example/src/singleton.cpp b/01-singleton-classic-example/src/singleton.cpp index dd1c453..209384b 100644 --- a/01-singleton-classic-example/src/singleton.cpp +++ b/01-singleton-classic-example/src/singleton.cpp @@ -1,5 +1,7 @@ #include "singleton.h" #include +#include +#include Singleton Singleton::instance; @@ -7,8 +9,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/singleton-meyers-example/CMakeLists.txt b/02-singleton-meyers-example/CMakeLists.txt similarity index 53% rename from singleton-meyers-example/CMakeLists.txt rename to 02-singleton-meyers-example/CMakeLists.txt index 1df1f95..b63f082 100644 --- a/singleton-meyers-example/CMakeLists.txt +++ b/02-singleton-meyers-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-meyers-example VERSION 0.1.0 LANGUAGES CXX) +project(02-singleton-meyers-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-meyers-example +add_executable(02-singleton-meyers-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-meyers-example PRIVATE +target_include_directories(02-singleton-meyers-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/singleton-meyers-example/inc/singleton.h b/02-singleton-meyers-example/inc/singleton.h similarity index 96% rename from singleton-meyers-example/inc/singleton.h rename to 02-singleton-meyers-example/inc/singleton.h index 581bc25..1e0cffa 100644 --- a/singleton-meyers-example/inc/singleton.h +++ b/02-singleton-meyers-example/inc/singleton.h @@ -10,7 +10,7 @@ class Singleton { return instance; } - void func(); + void info(); private: Singleton(); diff --git a/singleton-meyers-example/src/main.cpp b/02-singleton-meyers-example/src/main.cpp similarity index 85% rename from singleton-meyers-example/src/main.cpp rename to 02-singleton-meyers-example/src/main.cpp index 35e0b57..9dbf7b1 100644 --- a/singleton-meyers-example/src/main.cpp +++ b/02-singleton-meyers-example/src/main.cpp @@ -4,7 +4,7 @@ #include /* - * Meyer’s Singleton - Example with STATIC LOCAL VARIABLE + * Meyer’s Singleton - Example with static local variable * Static memory allocation * Lazy initialization * Singleton is created only after first call of getInstance() and destroyed after main() call @@ -20,7 +20,7 @@ int main() { // Launch 10 threads for (int i = 0; i < 10; ++i) { - threads.emplace_back([]() { Singleton::getInstance().func(); }); + threads.emplace_back([]() { Singleton::getInstance().info(); }); } for (auto& t : threads) { diff --git a/singleton-meyers-example/src/singleton.cpp b/02-singleton-meyers-example/src/singleton.cpp similarity index 50% rename from singleton-meyers-example/src/singleton.cpp rename to 02-singleton-meyers-example/src/singleton.cpp index bd9c324..61a93b7 100644 --- a/singleton-meyers-example/src/singleton.cpp +++ b/02-singleton-meyers-example/src/singleton.cpp @@ -1,12 +1,14 @@ #include "singleton.h" #include +#include +#include Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/CMakeLists.txt b/CMakeLists.txt index f096e57..166d320 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,8 @@ project(cpp-singleton-pattern-examples ) add_subdirectory(01-singleton-classic-example) +add_subdirectory(02-singleton-meyers-example) add_subdirectory(singleton-cherno-example) -add_subdirectory(singleton-meyers-example) add_subdirectory(singleton-classic-dynamic-example) add_subdirectory(singleton-dclp-example) add_subdirectory(singleton-smart-pointer-example) \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 12a570f..4994fb9 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -5,7 +5,6 @@ "minor": 21, "patch": 0 }, - "configurePresets": [ { "name": "base-config", @@ -13,12 +12,11 @@ "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "CMAKE_CXX_STANDARD": "11", + "CMAKE_CXX_STANDARD": "20", "CMAKE_CXX_STANDARD_REQUIRED": "ON", "CMAKE_CXX_EXTENSIONS": "OFF" } }, - { "name": "linux-ninja-debug", "displayName": "Linux (Ninja) - Debug", @@ -33,7 +31,6 @@ "CMAKE_BUILD_TYPE": "Debug" } }, - { "name": "windows-msvc", "displayName": "Windows (VS 2022) - Debug", @@ -45,7 +42,6 @@ }, "generator": "Visual Studio 17 2022" }, - { "name": "windows-ninja-debug", "displayName": "Windows (Ninja + GCC) - Debug", @@ -61,7 +57,6 @@ } } ], - "buildPresets": [ { "name": "linux-ninja-debug", @@ -80,4 +75,4 @@ "jobs": 0 } ] -} +} \ No newline at end of file diff --git a/README.md b/README.md index 0f68230..929a6f9 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,44 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways ## πŸ” Overview ### 1️⃣ singleton-classic-example -* πŸ”³ Singleton with a **static member instance**. + +* πŸ”³ Singleton with a static member instance. + * 🧩 Static member variable + * πŸ’Ύ Static memory allocation -* ⚑ Eager initialization (constructed before `main` starts) -* 🧼 Automatic destruction after `main` exits -* πŸ”’ Initialization is Thread-safe β†’ The static member is initialized before `main` in a single-threaded context, so no construction race is possible. -* ⚠️ Can suffer from the **Static Initialization Order Fiasco** β†’ If the singleton instance is accessed during the initialization of another static object, it may lead to undefined behavior due to the order of initialization. -### ⭐ singleton-meyers-example -* Meyers Singleton β€” the simplest and safest modern C++ singleton implementation. +* ⚑ Eager initialization β†’ constructed before `main` starts + +* 🧼 Automatic cleanup β†’ destroyed after `main` exits + +* πŸ”’ Initialization is Thread-safe + * The static member is initialized before `main` in a single-threaded context, so no construction race is possible. + +* ⚠️ Can suffer from the *Static Initialization Order Fiasco* + * If the singleton instance is accessed during the initialization of another static object, it may lead to UB due to the order of initialization. + +* ⚠️ A symmetric problem, it can suffer from *Static Destruction Order Fiasco*. + * If one static object's destructor calls another static that has been destroyed, it results in UB. + +### 2️⃣ singleton-meyers-example + +* πŸ”³ Meyer's Singleton β†’ The simplest and safest modern C++ singleton implementation. + * 🧩 Static local variable + * πŸ’Ύ Static memory allocation -* ⏳ Lazy initialization -* 🧼 Automatic cleanup (destroyed after main() exits) -* πŸ”’ Thread-safe since C++11 -* A function-local static variable is initialized exactly once, even in a multi-threaded environment. -* 🟒 This is the best and simplest way to implement a singleton in C++11 and later. + +* ⏳ Lazy initialization β†’ constructed only on first call to `getInstance()` + +* 🧼 Automatic cleanup β†’ destroyed after `main` exits + +* πŸ”’ Initialization is Thread-safe since C++11 + * A function-local static variable is initialized exactly once, even in a multi-threaded environment. + +* The Meyer's Singleton fixes *Static Initialization Order Fiasco*, but can suffer from *Static Destruction Order Fiasco*. + +* πŸ‘Œ This is the best and simplest way to implement a singleton in C++11 and later. ### ⭐ singleton-cherno-example * Cherno-style Singleton - https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY From f8d368a84c1ca18a38c42f2c2ec7ae7d54e7985f Mon Sep 17 00:00:00 2001 From: Ionut Tomos Date: Sat, 7 Mar 2026 23:12:04 +0200 Subject: [PATCH 3/8] Update README.md --- README.md | 48 +++++++++++++++--------------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 929a6f9..7a80120 100644 --- a/README.md +++ b/README.md @@ -6,42 +6,24 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways ### 1️⃣ singleton-classic-example -* πŸ”³ Singleton with a static member instance. - -* 🧩 Static member variable - -* πŸ’Ύ Static memory allocation - -* ⚑ Eager initialization β†’ constructed before `main` starts - -* 🧼 Automatic cleanup β†’ destroyed after `main` exits - -* πŸ”’ Initialization is Thread-safe - * The static member is initialized before `main` in a single-threaded context, so no construction race is possible. - -* ⚠️ Can suffer from the *Static Initialization Order Fiasco* - * If the singleton instance is accessed during the initialization of another static object, it may lead to UB due to the order of initialization. - -* ⚠️ A symmetric problem, it can suffer from *Static Destruction Order Fiasco*. - * If one static object's destructor calls another static that has been destroyed, it results in UB. +* πŸ”³ **Singleton with a static member instance.** +* 🧩 **Static member variable** +* πŸ’Ύ **Static memory allocation** +* ⚑ **Eager initialization** β†’ constructed before `main` starts +* 🧼 **Automatic cleanup** β†’ destroyed after `main` exits +* πŸ”’ **Initialization is Thread-safe** β†’ The static member is initialized before `main` in a single-threaded context, so no construction race is possible. +* ⚠️ **Static Initialization Order Fiasco** β†’ 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. +* ⚠️ **Static Destruction Order Fiasco** β†’ A symmetric problem, If one static object's destructor calls another static that has been destroyed, it results in UB. ### 2️⃣ singleton-meyers-example -* πŸ”³ Meyer's Singleton β†’ The simplest and safest modern C++ singleton implementation. - -* 🧩 Static local variable - -* πŸ’Ύ Static memory allocation - -* ⏳ Lazy initialization β†’ constructed only on first call to `getInstance()` - -* 🧼 Automatic cleanup β†’ destroyed after `main` exits - -* πŸ”’ Initialization is Thread-safe since C++11 - * A function-local static variable is initialized exactly once, even in a multi-threaded environment. - -* The Meyer's Singleton fixes *Static Initialization Order Fiasco*, but can suffer from *Static Destruction Order Fiasco*. - +* πŸ”³ **Meyer's Singleton** β†’ The simplest and safest modern C++ singleton implementation. +* 🧩 **Static local variable** +* πŸ’Ύ **Static memory allocation** +* ⏳ **Lazy initialization** β†’ constructed only on first call to `getInstance()` +* 🧼 **Automatic cleanup** β†’ destroyed after `main` exits +* πŸ”’ **Initialization is Thread-safe since C++11** β†’ A function-local static variable is initialized exactly once, even in a multi-threaded environment. +* ⚠️ The Meyer's Singleton fixes **Static Initialization Order Fiasco**, but can suffer from **Static Destruction Order Fiasco**. * πŸ‘Œ This is the best and simplest way to implement a singleton in C++11 and later. ### ⭐ singleton-cherno-example From deae7868e1ae0d15f101975152249fa563ac8331 Mon Sep 17 00:00:00 2001 From: Ionut Tomos Date: Sat, 7 Mar 2026 23:41:53 +0200 Subject: [PATCH 4/8] Reworked example 03. --- .../CMakeLists.txt | 14 +++++++ .../inc/singleton.h | 2 +- .../src/main.cpp | 8 ++-- .../src/singleton.cpp | 6 ++- CMakeLists.txt | 4 +- README.md | 39 +++++++++++-------- .../CMakeLists.txt | 14 ------- 7 files changed, 49 insertions(+), 38 deletions(-) create mode 100644 03-singleton-classic-dynamic-example/CMakeLists.txt rename {singleton-classic-dynamic-example => 03-singleton-classic-dynamic-example}/inc/singleton.h (95%) rename {singleton-classic-dynamic-example => 03-singleton-classic-dynamic-example}/src/main.cpp (70%) rename {singleton-classic-dynamic-example => 03-singleton-classic-dynamic-example}/src/singleton.cpp (70%) delete mode 100644 singleton-classic-dynamic-example/CMakeLists.txt diff --git a/03-singleton-classic-dynamic-example/CMakeLists.txt b/03-singleton-classic-dynamic-example/CMakeLists.txt new file mode 100644 index 0000000..c1dc5b0 --- /dev/null +++ b/03-singleton-classic-dynamic-example/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.21) +project(03-singleton-classic-dynamic-example VERSION 0.1.0 LANGUAGES CXX) + +# Export compile commands for clangd +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_executable(03-singleton-classic-dynamic-example + src/main.cpp + src/singleton.cpp +) + +target_include_directories(03-singleton-classic-dynamic-example PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) \ No newline at end of file diff --git a/singleton-classic-dynamic-example/inc/singleton.h b/03-singleton-classic-dynamic-example/inc/singleton.h similarity index 95% rename from singleton-classic-dynamic-example/inc/singleton.h rename to 03-singleton-classic-dynamic-example/inc/singleton.h index 888e76c..47f1216 100644 --- a/singleton-classic-dynamic-example/inc/singleton.h +++ b/03-singleton-classic-dynamic-example/inc/singleton.h @@ -7,7 +7,7 @@ class Singleton { static Singleton& getInstance(); static void delInstance(); - void func(); + void info(); private: Singleton(); diff --git a/singleton-classic-dynamic-example/src/main.cpp b/03-singleton-classic-dynamic-example/src/main.cpp similarity index 70% rename from singleton-classic-dynamic-example/src/main.cpp rename to 03-singleton-classic-dynamic-example/src/main.cpp index 5ed9220..1237d7b 100644 --- a/singleton-classic-dynamic-example/src/main.cpp +++ b/03-singleton-classic-dynamic-example/src/main.cpp @@ -2,18 +2,18 @@ #include /* - * Example with STATIC MEMBER VARIABLE + * Example with static member pointer * Dynamic memory allocation * Lazy initialization * Singleton is created only after first call of getInstance() and destroyed by calling delInstance() - * Thread-safety not guaranteed + * Not Thread-safe */ int main() { std::cout << "--- main start ---" << std::endl; - Singleton::getInstance().func(); - Singleton::getInstance().func(); + Singleton::getInstance().info(); + Singleton::getInstance().info(); Singleton::delInstance(); std::cout << "--- main end ---" << std::endl; diff --git a/singleton-classic-dynamic-example/src/singleton.cpp b/03-singleton-classic-dynamic-example/src/singleton.cpp similarity index 70% rename from singleton-classic-dynamic-example/src/singleton.cpp rename to 03-singleton-classic-dynamic-example/src/singleton.cpp index c3f6d80..b57f5cb 100644 --- a/singleton-classic-dynamic-example/src/singleton.cpp +++ b/03-singleton-classic-dynamic-example/src/singleton.cpp @@ -1,5 +1,7 @@ #include "singleton.h" #include +#include +#include Singleton* Singleton::instance = nullptr; @@ -21,8 +23,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/CMakeLists.txt b/CMakeLists.txt index 166d320..8247572 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,9 @@ project(cpp-singleton-pattern-examples add_subdirectory(01-singleton-classic-example) add_subdirectory(02-singleton-meyers-example) +add_subdirectory(03-singleton-classic-dynamic-example) + add_subdirectory(singleton-cherno-example) -add_subdirectory(singleton-classic-dynamic-example) + add_subdirectory(singleton-dclp-example) add_subdirectory(singleton-smart-pointer-example) \ No newline at end of file diff --git a/README.md b/README.md index 7a80120..0a23d8c 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,38 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * πŸ”³ **Singleton with a static member instance.** * 🧩 **Static member variable** * πŸ’Ύ **Static memory allocation** -* ⚑ **Eager initialization** β†’ constructed before `main` starts +* ⚑ **Eager initialization** β†’ created before `main` starts * 🧼 **Automatic cleanup** β†’ destroyed after `main` exits -* πŸ”’ **Initialization is Thread-safe** β†’ The static member is initialized before `main` in a single-threaded context, so no construction race is possible. -* ⚠️ **Static Initialization Order Fiasco** β†’ 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. -* ⚠️ **Static Destruction Order Fiasco** β†’ A symmetric problem, If one static object's destructor calls another static that has been destroyed, it results in UB. +* πŸ”’ **Initialization is Thread-safe** + * The static member is initialized before `main` in a single-threaded context, so no construction race is possible. +* ⚠️ **Static Initialization Order Fiasco** + * 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. +* ⚠️ **Static Destruction Order Fiasco** + * A symmetric problem, If one static object's destructor calls another static that has been destroyed, it results in UB. ### 2️⃣ singleton-meyers-example * πŸ”³ **Meyer's Singleton** β†’ The simplest and safest modern C++ singleton implementation. * 🧩 **Static local variable** * πŸ’Ύ **Static memory allocation** -* ⏳ **Lazy initialization** β†’ constructed only on first call to `getInstance()` +* ⏳ **Lazy initialization** β†’ created only on first call to `getInstance()` * 🧼 **Automatic cleanup** β†’ destroyed after `main` exits -* πŸ”’ **Initialization is Thread-safe since C++11** β†’ A function-local static variable is initialized exactly once, even in a multi-threaded environment. +* πŸ”’ **Initialization is Thread-safe since C++11** + * A function-local static variable is initialized exactly once, even in a multi-threaded environment. * ⚠️ The Meyer's Singleton fixes **Static Initialization Order Fiasco**, but can suffer from **Static Destruction Order Fiasco**. -* πŸ‘Œ This is the best and simplest way to implement a singleton in C++11 and later. + +### 3️⃣ singleton-classic-dynamic-example +* **Singleton with a static member pointer** β†’ dynamically allocated on first use. +* 🧩 **Static member pointer** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ created only on first call to `getInstance()` +* 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` +* ⚠️ **Not Thread-safe** + * Two threads could call `delInstance()` simultaneously, or one calls `getInstance()` while another calls `delInstance()`, leading to a race on the pointer. +* ⚠️ **Static Destruction Order Fiasco** + * If another static's destructor calls `getInstance()` after `delInstance()` has run, you're dereferencing a deleted pointer, right back to UB. +* ❗ Not recommended for multi-threaded applications. + ### ⭐ singleton-cherno-example * Cherno-style Singleton - https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY @@ -35,15 +51,6 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * ⚠️ Not thread-safe * πŸ”΄ Suitable only for single-threaded -### ⭐ singleton-classic-dynamic-example -* Singleton with a static member pointer β€” dynamically allocated on first use. -* 🧩 Static member pointer -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization (created only on first call to getInstance()) -* 🧹 Requires manual destruction via delInstance() -* ⚠️ Not thread-safe -* πŸ”΄ Not recommended for multi-threaded applications - ### ⭐ singleton-dclp-example * Double-Checked Locking Pattern (DCLP) β€” classic but unsafe lazy-initialization pattern. * 🧩 Static member pointer diff --git a/singleton-classic-dynamic-example/CMakeLists.txt b/singleton-classic-dynamic-example/CMakeLists.txt deleted file mode 100644 index ae938e0..0000000 --- a/singleton-classic-dynamic-example/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -project(singleton-classic-dynamic-example VERSION 0.1.0 LANGUAGES CXX) - -# Export compile commands for clangd -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -add_executable(singleton-classic-dynamic-example - src/main.cpp - src/singleton.cpp -) - -target_include_directories(singleton-classic-dynamic-example PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/inc -) \ No newline at end of file From cf5fce91714601234c92356a339b35e897d3c843 Mon Sep 17 00:00:00 2001 From: Ionut Tomos Date: Sun, 8 Mar 2026 00:02:35 +0200 Subject: [PATCH 5/8] Reworked example 05. --- .../src/singleton.cpp | 3 +- 04-singleton-cherno-example/CMakeLists.txt | 11 ++++ .../inc/singleton.h | 2 +- .../src/main.cpp | 8 +-- .../src/singleton.cpp | 5 +- .../CMakeLists.txt | 6 +-- .../inc/singleton.h | 5 +- .../src/main.cpp | 2 +- .../src/singleton.cpp | 19 +++---- CMakeLists.txt | 6 +-- README.md | 50 +++++++++---------- singleton-cherno-example/CMakeLists.txt | 11 ---- 12 files changed, 61 insertions(+), 67 deletions(-) create mode 100644 04-singleton-cherno-example/CMakeLists.txt rename {singleton-cherno-example => 04-singleton-cherno-example}/inc/singleton.h (95%) rename {singleton-cherno-example => 04-singleton-cherno-example}/src/main.cpp (69%) rename {singleton-cherno-example => 04-singleton-cherno-example}/src/singleton.cpp (76%) rename {singleton-dclp-example => 05-singleton-dclp-example}/CMakeLists.txt (53%) rename {singleton-dclp-example => 05-singleton-dclp-example}/inc/singleton.h (76%) rename {singleton-dclp-example => 05-singleton-dclp-example}/src/main.cpp (93%) rename {singleton-dclp-example => 05-singleton-dclp-example}/src/singleton.cpp (67%) delete mode 100644 singleton-cherno-example/CMakeLists.txt diff --git a/03-singleton-classic-dynamic-example/src/singleton.cpp b/03-singleton-classic-dynamic-example/src/singleton.cpp index b57f5cb..d3ec175 100644 --- a/03-singleton-classic-dynamic-example/src/singleton.cpp +++ b/03-singleton-classic-dynamic-example/src/singleton.cpp @@ -1,6 +1,5 @@ #include "singleton.h" #include -#include #include Singleton* Singleton::instance = nullptr; @@ -24,7 +23,7 @@ Singleton::Singleton() { } void Singleton::info() { - std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; + std::cout << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/04-singleton-cherno-example/CMakeLists.txt b/04-singleton-cherno-example/CMakeLists.txt new file mode 100644 index 0000000..cc8d9c5 --- /dev/null +++ b/04-singleton-cherno-example/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.21) +project(04-singleton-cherno-example VERSION 0.1.0 LANGUAGES CXX) + +add_executable(04-singleton-cherno-example + src/main.cpp + src/singleton.cpp +) + +target_include_directories(04-singleton-cherno-example PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) \ No newline at end of file diff --git a/singleton-cherno-example/inc/singleton.h b/04-singleton-cherno-example/inc/singleton.h similarity index 95% rename from singleton-cherno-example/inc/singleton.h rename to 04-singleton-cherno-example/inc/singleton.h index 60be0e0..9e0e1e7 100644 --- a/singleton-cherno-example/inc/singleton.h +++ b/04-singleton-cherno-example/inc/singleton.h @@ -7,7 +7,7 @@ class Singleton { static Singleton& getInstance(); static void delInstance(); - void func(); + void info(); private: Singleton(); diff --git a/singleton-cherno-example/src/main.cpp b/04-singleton-cherno-example/src/main.cpp similarity index 69% rename from singleton-cherno-example/src/main.cpp rename to 04-singleton-cherno-example/src/main.cpp index 4094582..b78239e 100644 --- a/singleton-cherno-example/src/main.cpp +++ b/04-singleton-cherno-example/src/main.cpp @@ -2,18 +2,18 @@ #include /* - * Example from Cherno with STATIC GLOBAL VARIABLE + * Example from Cherno with static global variable in cpp file * Dynamic memory allocation * Lazy initialization * Singleton is created only after first call of getInstance() and destroyed by calling delInstance() - * Thread-safety not guaranteed + * Not Thread-safe */ int main() { std::cout << "--- main start ---" << std::endl; - Singleton::getInstance().func(); - + Singleton::getInstance().info(); + Singleton::getInstance().info(); Singleton::delInstance(); std::cout << "--- main end ---" << std::endl; diff --git a/singleton-cherno-example/src/singleton.cpp b/04-singleton-cherno-example/src/singleton.cpp similarity index 76% rename from singleton-cherno-example/src/singleton.cpp rename to 04-singleton-cherno-example/src/singleton.cpp index 07d3221..cbcb82e 100644 --- a/singleton-cherno-example/src/singleton.cpp +++ b/04-singleton-cherno-example/src/singleton.cpp @@ -1,5 +1,6 @@ #include "../inc/singleton.h" #include +#include /* it's private to this file */ static Singleton* instance = nullptr; @@ -22,8 +23,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::cout << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/singleton-dclp-example/CMakeLists.txt b/05-singleton-dclp-example/CMakeLists.txt similarity index 53% rename from singleton-dclp-example/CMakeLists.txt rename to 05-singleton-dclp-example/CMakeLists.txt index b7f4e2a..8f161c0 100644 --- a/singleton-dclp-example/CMakeLists.txt +++ b/05-singleton-dclp-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-dclp-example VERSION 0.1.0 LANGUAGES CXX) +project(05-singleton-dclp-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-dclp-example +add_executable(05-singleton-dclp-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-dclp-example PRIVATE +target_include_directories(05-singleton-dclp-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/singleton-dclp-example/inc/singleton.h b/05-singleton-dclp-example/inc/singleton.h similarity index 76% rename from singleton-dclp-example/inc/singleton.h rename to 05-singleton-dclp-example/inc/singleton.h index 986de48..4c1d136 100644 --- a/singleton-dclp-example/inc/singleton.h +++ b/05-singleton-dclp-example/inc/singleton.h @@ -4,13 +4,12 @@ class Singleton { public: - Singleton(Singleton& other) = delete; /* Deleted copy constructor. */ + Singleton(Singleton& other) = delete; /* Deleted copy constructor. */ Singleton& operator=(const Singleton&) = delete; /* Deleted copy assigment operator. */ static Singleton& getInstance(); static void delInstance(); - - void func(); + void info(); private: Singleton(); diff --git a/singleton-dclp-example/src/main.cpp b/05-singleton-dclp-example/src/main.cpp similarity index 93% rename from singleton-dclp-example/src/main.cpp rename to 05-singleton-dclp-example/src/main.cpp index 532c03e..5a3629e 100644 --- a/singleton-dclp-example/src/main.cpp +++ b/05-singleton-dclp-example/src/main.cpp @@ -22,7 +22,7 @@ int main() { // Launch 10 threads for (int i = 0; i < 10; ++i) { - threads.emplace_back([]() { Singleton::getInstance().func(); }); + threads.emplace_back([]() { Singleton::getInstance().info(); }); } for (auto& t : threads) { diff --git a/singleton-dclp-example/src/singleton.cpp b/05-singleton-dclp-example/src/singleton.cpp similarity index 67% rename from singleton-dclp-example/src/singleton.cpp rename to 05-singleton-dclp-example/src/singleton.cpp index 9e659de..7c5298b 100644 --- a/singleton-dclp-example/src/singleton.cpp +++ b/05-singleton-dclp-example/src/singleton.cpp @@ -1,16 +1,15 @@ #include "singleton.h" #include +#include +#include Singleton* Singleton::instance = nullptr; std::mutex Singleton::mtx; - Singleton& Singleton::getInstance() { - if (!instance) - { + if (!instance) { std::lock_guard lock(mtx); - if (!instance) - { + if (!instance) { instance = new Singleton(); } } @@ -18,11 +17,9 @@ Singleton& Singleton::getInstance() { } void Singleton::delInstance() { - if (instance) - { + if (instance) { std::lock_guard lock(mtx); - if (instance) - { + if (instance) { delete instance; instance = nullptr; } @@ -33,8 +30,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/CMakeLists.txt b/CMakeLists.txt index 8247572..f37008f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,6 @@ project(cpp-singleton-pattern-examples add_subdirectory(01-singleton-classic-example) add_subdirectory(02-singleton-meyers-example) add_subdirectory(03-singleton-classic-dynamic-example) - -add_subdirectory(singleton-cherno-example) - -add_subdirectory(singleton-dclp-example) +add_subdirectory(04-singleton-cherno-example) +add_subdirectory(05-singleton-dclp-example) add_subdirectory(singleton-smart-pointer-example) \ No newline at end of file diff --git a/README.md b/README.md index 0a23d8c..a218e02 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,8 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * πŸ”³ **Singleton with a static member instance.** * 🧩 **Static member variable** * πŸ’Ύ **Static memory allocation** -* ⚑ **Eager initialization** β†’ created before `main` starts -* 🧼 **Automatic cleanup** β†’ destroyed after `main` exits +* ⚑ **Eager initialization** β†’ Created before `main` starts +* 🧼 **Automatic cleanup** β†’ Destroyed after `main` exits * πŸ”’ **Initialization is Thread-safe** * The static member is initialized before `main` in a single-threaded context, so no construction race is possible. * ⚠️ **Static Initialization Order Fiasco** @@ -23,17 +23,17 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * πŸ”³ **Meyer's Singleton** β†’ The simplest and safest modern C++ singleton implementation. * 🧩 **Static local variable** * πŸ’Ύ **Static memory allocation** -* ⏳ **Lazy initialization** β†’ created only on first call to `getInstance()` -* 🧼 **Automatic cleanup** β†’ destroyed after `main` exits +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧼 **Automatic cleanup** β†’ Destroyed after `main` exits * πŸ”’ **Initialization is Thread-safe since C++11** * A function-local static variable is initialized exactly once, even in a multi-threaded environment. * ⚠️ The Meyer's Singleton fixes **Static Initialization Order Fiasco**, but can suffer from **Static Destruction Order Fiasco**. ### 3️⃣ singleton-classic-dynamic-example -* **Singleton with a static member pointer** β†’ dynamically allocated on first use. +* **Singleton with a static member pointer** β†’ Dynamically allocated on first use. * 🧩 **Static member pointer** * πŸ’Ύ **Dynamic memory allocation** -* ⏳ **Lazy initialization** β†’ created only on first call to `getInstance()` +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` * 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` * ⚠️ **Not Thread-safe** * Two threads could call `delInstance()` simultaneously, or one calls `getInstance()` while another calls `delInstance()`, leading to a race on the pointer. @@ -42,25 +42,25 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * ❗ Not recommended for multi-threaded applications. -### ⭐ singleton-cherno-example -* Cherno-style Singleton - https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY -* 🧩 Static global variable -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization -* 🧹 Manual destruction required (via delInstance()) -* ⚠️ Not thread-safe -* πŸ”΄ Suitable only for single-threaded - -### ⭐ singleton-dclp-example -* Double-Checked Locking Pattern (DCLP) β€” classic but unsafe lazy-initialization pattern. -* 🧩 Static member pointer -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization -* 🧹 Destroyed manually via delInstance() -* ⚠️ Not thread-safe in C++ β€” suffers from data races and reordering issues -* ❌ DCLP is unreliable because multiple threads may observe a partially constructed object -* β›” Obsoleted by C++11 (local static initialization is the correct modern solution) -* Reference: [https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004](https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf) +### 4️⃣ singleton-cherno-example +* **Cherno-style Singleton** β†’ [Why I don't like Singletons - Youtube](https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY) +* 🧩 **Static global variable** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` +* ⚠️ **Not Thread-safe** β†’ Same as [singleton-classic-dynamic-example](#3️⃣-singleton-classic-dynamic-example) +* ❗ Not recommended for multi-threaded applications. + +### 5️⃣ singleton-dclp-example +* **Singleton with Double-Checked Locking Pattern (DCLP)** β†’ Classic but unsafe lazy-initialization pattern. +* 🧩 **Static member pointer** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` +* ⚠️ **Not Thread-safe** β†’ Suffers from data races and reordering issues. +* ❌ **DCLP is unreliable** β†’ Multiple threads may observe a partially constructed object. +* β›” **Obsoleted by C++11** β†’ Local static initialization is the correct modern solution. +* 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) ### ⭐ singleton-smart-pointer-example * Singleton using a static local smart pointer diff --git a/singleton-cherno-example/CMakeLists.txt b/singleton-cherno-example/CMakeLists.txt deleted file mode 100644 index 7993ba1..0000000 --- a/singleton-cherno-example/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -project(singleton-cherno-example VERSION 0.1.0 LANGUAGES CXX) - -add_executable(singleton-cherno-example - src/main.cpp - src/singleton.cpp -) - -target_include_directories(singleton-cherno-example PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/inc -) \ No newline at end of file From 023b723c3acc53923dfb4adb145cf927e73ee523 Mon Sep 17 00:00:00 2001 From: Ionut Tomos Date: Sun, 8 Mar 2026 00:27:58 +0200 Subject: [PATCH 6/8] Changed example. --- .../CMakeLists.txt | 6 ++-- 06-singleton-leaky-example/inc/singleton.h | 19 +++++++++++++ .../src/main.cpp | 8 +++--- 06-singleton-leaky-example/src/singleton.cpp | 16 +++++++++++ CMakeLists.txt | 2 +- README.md | 28 +++++++++++-------- .../inc/singleton.h | 14 ---------- .../src/singleton.cpp | 21 -------------- 8 files changed, 60 insertions(+), 54 deletions(-) rename {singleton-smart-pointer-example => 06-singleton-leaky-example}/CMakeLists.txt (51%) create mode 100644 06-singleton-leaky-example/inc/singleton.h rename {singleton-smart-pointer-example => 06-singleton-leaky-example}/src/main.cpp (59%) create mode 100644 06-singleton-leaky-example/src/singleton.cpp delete mode 100644 singleton-smart-pointer-example/inc/singleton.h delete mode 100644 singleton-smart-pointer-example/src/singleton.cpp diff --git a/singleton-smart-pointer-example/CMakeLists.txt b/06-singleton-leaky-example/CMakeLists.txt similarity index 51% rename from singleton-smart-pointer-example/CMakeLists.txt rename to 06-singleton-leaky-example/CMakeLists.txt index 415dbd2..cbbc7e8 100644 --- a/singleton-smart-pointer-example/CMakeLists.txt +++ b/06-singleton-leaky-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-smart-pointer-example VERSION 0.1.0 LANGUAGES CXX) +project(06-singleton-leaky-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-smart-pointer-example +add_executable(06-singleton-leaky-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-smart-pointer-example PRIVATE +target_include_directories(06-singleton-leaky-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/06-singleton-leaky-example/inc/singleton.h b/06-singleton-leaky-example/inc/singleton.h new file mode 100644 index 0000000..23923ad --- /dev/null +++ b/06-singleton-leaky-example/inc/singleton.h @@ -0,0 +1,19 @@ +#pragma once + +class Singleton { +public: + Singleton(const Singleton&) = delete; /* Deleted copy constructor. */ + Singleton& operator=(const Singleton&) = delete; /* Deleted copy assigment operator. */ + + static Singleton& getInstance() { + /* Static local variable is thread safe >= C++11 */ + static Singleton* instance = new Singleton(); + return *instance; + } + + void info(); + +private: + Singleton(); + ~Singleton(); // This will NEVER be called +}; \ No newline at end of file diff --git a/singleton-smart-pointer-example/src/main.cpp b/06-singleton-leaky-example/src/main.cpp similarity index 59% rename from singleton-smart-pointer-example/src/main.cpp rename to 06-singleton-leaky-example/src/main.cpp index 585cca8..9131602 100644 --- a/singleton-smart-pointer-example/src/main.cpp +++ b/06-singleton-leaky-example/src/main.cpp @@ -4,11 +4,11 @@ #include /* - * Example with STATIC LOCAL VARIABLE that is a SMART POINTER + * Leaky Singleton - Example with static local pointer * Dynamic memory allocation * Lazy initialization - * Singleton is created in the first call of getInstance() and destroyed automatically after main() call - * Thread-safety IS guaranteed (C++11+) + * Singleton is created only after first call of getInstance() and never destroyed. + * Initialization is Thread-safe - Thread safety is guaranteed since C++11. */ int main() { @@ -18,7 +18,7 @@ int main() { // Launch 10 threads for (int i = 0; i < 10; ++i) { - threads.emplace_back([]() { Singleton::getInstance().func(); }); + threads.emplace_back([]() { Singleton::getInstance().info(); }); } for (auto& t : threads) { diff --git a/06-singleton-leaky-example/src/singleton.cpp b/06-singleton-leaky-example/src/singleton.cpp new file mode 100644 index 0000000..61a93b7 --- /dev/null +++ b/06-singleton-leaky-example/src/singleton.cpp @@ -0,0 +1,16 @@ +#include "singleton.h" +#include +#include +#include + +Singleton::Singleton() { + std::cout << "Singleton created." << std::endl; +} + +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; +} + +Singleton::~Singleton() { + std::cout << "Singleton destroyed." << std::endl; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f37008f..8c62a08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,4 +10,4 @@ add_subdirectory(02-singleton-meyers-example) add_subdirectory(03-singleton-classic-dynamic-example) add_subdirectory(04-singleton-cherno-example) add_subdirectory(05-singleton-dclp-example) -add_subdirectory(singleton-smart-pointer-example) \ No newline at end of file +add_subdirectory(06-singleton-leaky-example) \ No newline at end of file diff --git a/README.md b/README.md index a218e02..b9c6029 100644 --- a/README.md +++ b/README.md @@ -62,13 +62,19 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * β›” **Obsoleted by C++11** β†’ Local static initialization is the correct modern solution. * 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) -### ⭐ singleton-smart-pointer-example -* Singleton using a static local smart pointer -* 🧩 Static local variable -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization (instance created on first getInstance() call) -* 🧼 Automatically destroyed after main() exits -* πŸ”’ Thread-safe initialization (C++11+) +### 6️⃣ singleton-leaky-example +* πŸ”³ **"Leaky" Singleton** β†’ A heap-allocated Meyer's Singleton. +* 🧩 **Static local pointer** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **No cleanup** β†’ You are intentionally leaking the memory. + * When the process ends, the Operating System reclaims the entire memory block anyway. "Leaking" at process exit is technically harmless. +* πŸ”’ **Initialization is Thread-safe since C++11** + * The local static pointer initialization is still protected by C++11's thread-safe static init guarantee, so the `new` only fires once, safely. +* ❎ **Avoids Static Initialization Order Fiasco** + * Since the destructor is never called, it can't try to access other dead objects during shutdown. + +--- ## βš™οΈ Prerequisites @@ -149,24 +155,24 @@ cmake --build --preset --target Example: ```bash -cmake --build --preset linux-ninja-debug --target singleton-meyers-example +cmake --build --preset linux-ninja-debug --target 02-singleton-meyers-example ``` ## πŸƒ Running Examples ### πŸ–₯️ Windows (MSVC) ```bash -build/windows-msvc/singleton-meyers-example/Debug/singleton-meyers-example.exe +build/windows-msvc/singleton-meyers-example/Debug/02-singleton-meyers-example.exe ``` ### πŸ–₯️ Windows (MinGW) ```bash -build/windows-ninja-debug/singleton-meyers-example/singleton-meyers-example.exe +build/windows-ninja-debug/singleton-meyers-example/02-singleton-meyers-example.exe ``` ### 🐧 Linux ```bash -./build/linux-ninja-debug/singleton-meyers-example/singleton-meyers-example +./build/linux-ninja-debug/singleton-meyers-example/02-singleton-meyers-example ``` diff --git a/singleton-smart-pointer-example/inc/singleton.h b/singleton-smart-pointer-example/inc/singleton.h deleted file mode 100644 index eb4db03..0000000 --- a/singleton-smart-pointer-example/inc/singleton.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -class Singleton { -public: - Singleton(const Singleton&) = delete; /* Deleted copy constructor. */ - Singleton& operator=(const Singleton&) = delete; /* Deleted copy assigment operator. */ - - static Singleton& getInstance(); - void func(); - ~Singleton(); - -private: - Singleton(); -}; \ No newline at end of file diff --git a/singleton-smart-pointer-example/src/singleton.cpp b/singleton-smart-pointer-example/src/singleton.cpp deleted file mode 100644 index c186def..0000000 --- a/singleton-smart-pointer-example/src/singleton.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "singleton.h" -#include -#include - -Singleton& Singleton::getInstance() { - // The "Magic Static" is thread-safe in C++11. - static std::unique_ptr instance(new Singleton()); - return *instance; -} - -Singleton::Singleton() { - std::cout << "Singleton created." << std::endl; -} - -void Singleton::func() { - std::cout << "Doing something..." << std::endl; -} - -Singleton::~Singleton() { - std::cout << "Singleton destroyed." << std::endl; -} \ No newline at end of file From b153e0958775ec7b9a5e6331390cf6f2adf31ec8 Mon Sep 17 00:00:00 2001 From: Aurelian-Ionut Tomos <37960915+tomoss@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:28:54 +0200 Subject: [PATCH 7/8] Fix spelling and formatting in README.md Corrected the spelling of 'Meyers' to 'Meyer's' and removed an extra space. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b9c6029..36e92c1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Singleton pattern examples in C++ -A collection of minimal, self-contained C++ examples demonstrating multiple ways to implement the Singleton design pattern. The repository includes modern, thread-safe techniques (Meyers Singleton) and legacy aproaches (Raw Pointers, Double-Checked Locking) for comparison. +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. ## πŸ” Overview From 7ad89e748268ce1a0589335c2c6ab0fd3b453797 Mon Sep 17 00:00:00 2001 From: Aurelian-Ionut Tomos <37960915+tomoss@users.noreply.github.com> Date: Sun, 8 Mar 2026 00:32:53 +0200 Subject: [PATCH 8/8] Fix typo in README regarding static initialization --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36e92c1..1a1cf93 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ A collection of minimal, self-contained C++ examples demonstrating multiple ways * When the process ends, the Operating System reclaims the entire memory block anyway. "Leaking" at process exit is technically harmless. * πŸ”’ **Initialization is Thread-safe since C++11** * The local static pointer initialization is still protected by C++11's thread-safe static init guarantee, so the `new` only fires once, safely. -* ❎ **Avoids Static Initialization Order Fiasco** +* ❎ **Avoids Static Initialization/Destruction Order Fiasco** * Since the destructor is never called, it can't try to access other dead objects during shutdown. ---