← All Patterns
creational1 participants

Singleton

Ensure a class has only one instance and provide a global point of access to it.

The Problem

Some resources must have exactly one instance — a log sink, a device handle, a thread pool. Global variables work but offer no lazy initialization, thread safety, or controlled destruction. Multiple instances of a Logger would interleave output; multiple thread pools would thrash the scheduler.

Structure

«singleton»Loggerinstance() log(msg)static Logger inst;Subsystem ASubsystem BSubsystem Cinstance()instance()instance()sameobjectreturned

Execution Walkthrough

1

Private ctor

Constructor is private — no external code can call new Logger(). Prevents accidental duplication.

2

Static instance

3

instance() call

4

Delete copies

5

Use anywhere

Code Comparison

Multiple Logger instances write to the same file with interleaved output and race conditions.

// BAD: every team creates their own logger
// subsystem_a.cpp
Logger logger_a("app.log");

// subsystem_b.cpp
Logger logger_b("app.log");  // same file, two streams

// Result: interleaved writes, seek races,
// duplicate log entries, file descriptor leak
logger_a.log("starting");
logger_b.log("starting");   // duplicated
// Output is non-deterministic

Full C++ Implementation

C++ · Singleton
#include <mutex>
#include <fstream>
#include <string_view>
#include <iostream>

class Logger {
  std::mutex    mu_;
  std::ofstream out_;

  Logger() : out_("app.log", std::ios::app) {}

public:
  static Logger& instance() {
    static Logger inst;  // thread-safe since C++11
    return inst;
  }

  Logger(const Logger&)            = delete;
  Logger& operator=(const Logger&) = delete;

  void log(std::string_view msg) {
    std::lock_guard lock{mu_};
    out_ << msg << '\n';
    out_.flush();
  }
};

// Any file, any thread — same object
void worker(int id) {
  Logger::instance().log(
    "worker " + std::to_string(id) + " started");
}

int main() {
  Logger::instance().log("=== start ===");
  std::thread t1(worker, 1);
  std::thread t2(worker, 2);
  t1.join(); t2.join();
  Logger::instance().log("=== end ===");
}

Participants

Singleton

Where it is used

Thread pools

A global worker pool constructed once at startup — all subsystems submit tasks to the same pool.

Logging systems

Valgrind's log sink, production loggers like spdlog's default logger — one sink, all writers.

Hardware drivers

A GPU device handle is initialized once — reopening the device for every subsystem would fail or leak.

Config registry

Parsed YAML config loaded once at boot — every module reads from the same registry without file I/O.

BuilderVisitor