Menu
Coddy logo textTech

Singleton Pattern

Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 91 of 104.

The Singleton pattern ensures that a class has only one instance throughout the entire program and provides a global point of access to it. This is useful for resources that should be shared, like a configuration manager, logger, or database connection pool.

The key idea is to make the constructor private so no one can create instances directly, then provide a static method that returns the single instance:

#include <iostream>
#include <string>

class Logger {
private:
    std::string logFile;
    
    // Private constructor - prevents direct instantiation
    Logger() : logFile("app.log") {}
    
    // Delete copy constructor and assignment operator
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // Static method to access the single instance
    static Logger& getInstance() {
        static Logger instance;  // Created once, lives until program ends
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

int main() {
    Logger::getInstance().log("Application started");
    
    Logger& logger = Logger::getInstance();
    logger.log("Processing data");
}

The static Logger instance inside getInstance() is a C++11 feature called "magic statics" - it's thread-safe and guaranteed to be initialized only once. By deleting the copy constructor and assignment operator, we prevent anyone from creating copies of our singleton.

Use Singleton sparingly - it introduces global state which can make testing harder. It's best suited for truly shared resources where having multiple instances would cause problems.

challenge icon

Challenge

Easy

Let's build a GameSettings singleton that manages configuration for a game application. This is a perfect use case for the Singleton pattern—you want exactly one settings manager that all parts of your game can access, ensuring consistent configuration throughout.

You'll organize your code across three files:

  • GameSettings.h: Define your singleton class that manages game configuration.

    Your GameSettings class should store three settings as private members: difficulty (string), volume (integer from 0-100), and fullscreen (boolean). Initialize them with default values: "Normal", 50, and false.

    Implement the Singleton pattern properly:

    • Make the constructor private
    • Delete the copy constructor and assignment operator
    • Provide a static getInstance() method that returns a reference to the single instance

    Add these public methods to interact with the settings:

    • setDifficulty(const std::string& diff) — updates the difficulty
    • setVolume(int vol) — updates the volume
    • setFullscreen(bool fs) — updates the fullscreen mode
    • displaySettings() — prints all current settings
  • GameModule.h: Create a separate module that accesses the singleton.

    Define a function called applyGamePreset that takes a preset name (string). This function should access the GameSettings singleton and configure it based on the preset:

    • "casual" — sets difficulty to "Easy", volume to 70, fullscreen to false
    • "competitive" — sets difficulty to "Hard", volume to 30, fullscreen to true
    • Any other preset — keeps the default settings unchanged

    This demonstrates how different parts of a program can access and modify the same singleton instance.

  • main.cpp: Bring everything together to show the singleton in action.

    Read two inputs:

    1. A preset name (string)
    2. A custom volume level (integer)

    First, display the initial default settings by calling displaySettings() on the singleton. Print a blank line after.

    Then apply the preset using your applyGamePreset function and display the settings again. Print a blank line after.

    Finally, override just the volume with the custom input value and display the final settings.

The displaySettings() method should output in this exact format:

Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]

For example, with inputs casual and 85:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Easy
Volume: 70
Fullscreen: no

Difficulty: Easy
Volume: 85
Fullscreen: no

With inputs competitive and 20:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Hard
Volume: 30
Fullscreen: yes

Difficulty: Hard
Volume: 20
Fullscreen: yes

Notice how both main.cpp and GameModule.h access the same singleton instance—changes made in one place are visible everywhere. This is the power of the Singleton pattern for shared resources like game settings.

Cheat sheet

The Singleton pattern ensures a class has only one instance throughout the program and provides global access to it. It's useful for shared resources like configuration managers, loggers, or database connection pools.

Key implementation steps:

  • Make the constructor private to prevent direct instantiation
  • Delete the copy constructor and assignment operator to prevent copies
  • Provide a static method that returns a reference to the single instance
class Logger {
private:
    std::string logFile;
    
    // Private constructor
    Logger() : logFile("app.log") {}
    
    // Delete copy constructor and assignment operator
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // Static method to access the single instance
    static Logger& getInstance() {
        static Logger instance;  // Created once, lives until program ends
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

// Usage
Logger::getInstance().log("Application started");

Logger& logger = Logger::getInstance();
logger.log("Processing data");

The static Logger instance inside getInstance() uses C++11 "magic statics"—it's thread-safe and guaranteed to be initialized only once.

Use Singleton sparingly as it introduces global state which can make testing harder. Best suited for truly shared resources where multiple instances would cause problems.

Try it yourself

#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"

using namespace std;

int main() {
    // Read inputs
    string presetName;
    int customVolume;
    cin >> presetName;
    cin >> customVolume;
    
    // TODO: Get the GameSettings singleton instance
    
    // TODO: Display initial default settings
    
    // TODO: Print a blank line
    
    // TODO: Apply the preset using applyGamePreset function
    
    // TODO: Display settings after applying preset
    
    // TODO: Print a blank line
    
    // TODO: Override the volume with customVolume
    
    // TODO: Display final settings
    
    return 0;
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming