Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C++

Viết lớp Singleton trong C++

Singleton Pattern đảm bảo một lớp chỉ có một instance duy nhất và cung cấp điểm truy cập toàn cục đến instance đó.

Cài đặt cơ bản (C++98 style)


#include <iostream>

class Singleton {
private:
    static Singleton* instance;  // Con trỏ static đến instance duy nhất
    int data;
    
    // Private constructor - không cho phép tạo từ bên ngoài
    Singleton() : data(0) {}
    
    // C++11: Xóa copy/move
    // Singleton(const Singleton&) = delete;
    // Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton* getInstance() {
        if (!instance) {
            instance = new Singleton();  // Lazy initialization
        }
        return instance;
    }
    
    int getData() const { return data; }
    void setData(int d) { data = d; }
    
    // Cleanup (tùy chọn)
    static void destroy() {
        delete instance;
        instance = nullptr;
    }
};

// Khởi tạo static member
Singleton* Singleton::instance = nullptr;

int main() {
    Singleton* s = Singleton::getInstance();
    std::cout << s->getData() << std::endl;  // 0
    
    s->setData(100);
    std::cout << s->getData() << std::endl;  // 100
    
    Singleton::destroy();
}

Kết quả

0
100

Vấn đề với cách trên

  • Không thread-safe: Hai thread cùng gọi getInstance() có thể tạo 2 instance
  • Rò rỉ bộ nhớ nếu quên destroy()
  • Thứ tự hủy static object không xác định

C++11: Thread-safe Singleton (Meyer's Singleton)


class Singleton {
private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
public:
    static Singleton& getInstance() {
        static Singleton instance;  // C++11: Khởi tạo thread-safe
        return instance;
    }
    
    int data = 0;
};

// Sử dụng:
Singleton& s = Singleton::getInstance();
s.data = 42;

Ưu điểm Meyer's Singleton

  • Thread-safe (C++11 đảm bảo static local init an toàn)
  • Không cần quản lý bộ nhớ thủ công
  • Hủy tự động khi chương trình kết thúc
  • Lazy initialization (tạo khi đầu tiên dùng)

Eager Initialization (Khởi tạo sớm)


class Singleton {
private:
    static Singleton instance;  // Tạo ngay khi load program
    Singleton() {}
public:
    static Singleton& getInstance() { return instance; }
};

Singleton Singleton::instance;  // Định nghĩa

Singleton với dependency injection (Testable)


class Logger {
public:
    virtual void log(const std::string& msg) = 0;
    virtual ~Logger() = default;
};

class FileLogger : public Logger {
    // ...
};

class App {
    Logger& logger;  // Dependency inject
public:
    App(Logger& l) : logger(l) {}
    void run() { logger.log("Start"); }
};

// Test: Truyền MockLogger
// Production: Truyền FileLogger

Khi nào dùng Singleton?

  • Logger, Configuration, Cache, Connection Pool
  • Hardware interface (GPU, GPIO...)
  • Khi thực sự cần một instance toàn cục

Khi nào KHÔNG dùng

  • Chỉ để tránh truyền tham số (global state ẩn)
  • Khi cần multiple instances cho test
  • Khi vi phạm Single Responsibility Principle

Tóm tắt

C++11+: Dùng Meyer's Singleton (static local trong function) — đơn giản, thread-safe, RAII. Tránh con trỏ raw + new/delete.