SyntaxStudy
Sign Up
C++ Static Members and the Singleton Pattern
C++ Beginner 1 min read

Static Members and the Singleton Pattern

Static data members belong to the class rather than to any particular instance. They are shared across all objects of that class and must be defined (and optionally initialised) outside the class body in a single translation unit. Static member functions similarly operate on the class level, can access only static data, and can be called without an instance using the class name and scope-resolution operator. A common use of static members is to track how many instances of a class currently exist, or to implement a factory that controls creation. The Singleton pattern takes this further, guaranteeing that at most one instance exists by making the constructor private and providing a static 'getInstance()' accessor. The C++11 standard guarantees that local static initialisation is thread-safe. Class constants are best declared as 'static constexpr' data members. Unlike 'static const' integral types which had special treatment in older standards, 'constexpr' makes the constant available at compile time and is usable in template arguments, array sizes, and 'switch' statements without requiring an out-of-class definition in C++17 and later.
Example
#include <iostream>
#include <string>

class Logger {
public:
    // C++11 thread-safe local-static Singleton
    static Logger& getInstance() {
        static Logger instance;   // constructed once, destroyed at program exit
        return instance;
    }

    void log(const std::string& msg) {
        ++messageCount_;
        std::cout << "[LOG #" << messageCount_ << "] " << msg << "\n";
    }

    int messageCount() const { return messageCount_; }

    // Delete copy/move to enforce singleton semantics
    Logger(const Logger&)            = delete;
    Logger& operator=(const Logger&) = delete;

private:
    Logger() : messageCount_(0) {
        std::cout << "Logger initialised\n";
    }

    int messageCount_;
};

class Config {
public:
    static constexpr int    MAX_RETRIES = 3;
    static constexpr double TIMEOUT_SEC = 30.0;

    static int instanceCount() { return count_; }

    Config()  { ++count_; }
    ~Config() { --count_; }

private:
    static int count_;   // definition in .cpp: int Config::count_ = 0;
};

int Config::count_ = 0;

int main() {
    Logger::getInstance().log("Application started");
    Logger::getInstance().log("Config loaded");

    {
        Config c1, c2;
        std::cout << "Config instances: " << Config::instanceCount() << "\n";
    }
    std::cout << "Config instances: " << Config::instanceCount() << "\n";
    std::cout << "MAX_RETRIES = " << Config::MAX_RETRIES << "\n";

    return 0;
}