본문 바로가기
-----ETC-----/C++ 고급 프로그래밍과 응용 프로젝트 시리즈

[C++ 고급 프로그래밍과 응용 프로젝트 시리즈] Day 8: 디자인 패턴 심화 - 싱글톤 패턴 (Singleton Pattern)

by cogito21_cpp 2024. 8. 1.
반응형

싱글톤 패턴 (Singleton Pattern)

싱글톤 패턴은 특정 클래스의 인스턴스가 하나만 존재하도록 보장하고, 그 인스턴스에 대한 전역 접근점을 제공하는 디자인 패턴입니다. 이는 전역 상태를 관리하거나, 리소스를 공유해야 하는 상황에서 유용합니다.

 

싱글톤 패턴의 특징

  1. 유일한 인스턴스: 클래스의 인스턴스가 하나만 존재함을 보장합니다.
  2. 전역 접근점: 유일한 인스턴스에 대한 전역 접근을 제공합니다.
  3. 게으른 초기화: 인스턴스가 처음 필요할 때 생성됩니다.

 

기본 싱글톤 패턴 구현

싱글톤 패턴을 구현하는 방법은 여러 가지가 있지만, 여기서는 가장 일반적인 구현 방법을 소개합니다.

 

싱글톤 클래스 정의

#include <iostream>
#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mtx;

    // 생성자를 private으로 정의하여 외부에서 객체 생성을 막습니다.
    Singleton() {}

public:
    // 복사 생성자와 대입 연산자를 삭제합니다.
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 유일한 인스턴스를 얻는 정적 메서드
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance;
    }

    void showMessage() {
        std::cout << "Hello from Singleton!" << std::endl;
    }
};

// 정적 멤버 변수 초기화
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

int main() {
    Singleton* s1 = Singleton::getInstance();
    s1->showMessage();

    Singleton* s2 = Singleton::getInstance();
    s2->showMessage();

    // 두 포인터가 동일한 인스턴스를 가리키는지 확인
    if (s1 == s2) {
        std::cout << "s1 and s2 are the same instance." << std::endl;
    }

    return 0;
}

 

이 예제에서 Singleton 클래스는 getInstance 메서드를 통해 유일한 인스턴스를 제공합니다. 인스턴스가 처음 필요할 때 생성되며, 이 과정은 스레드 안전하게 구현되었습니다.


싱글톤 패턴의 변형

싱글톤 패턴의 구현은 다양한 방법으로 변형될 수 있습니다. 몇 가지 일반적인 변형을 소개하겠습니다.

 

마이어스의 싱글톤 (Meyers' Singleton)

마이어스의 싱글톤은 C++11 이후의 표준에서 안전한 정적 지역 변수를 사용하여 싱글톤을 구현합니다. 이 방법은 간결하고, 스레드 안전성을 보장합니다.

#include <iostream>

class Singleton {
public:
    // 인스턴스를 얻는 정적 메서드
    static Singleton& getInstance() {
        static Singleton instance;  // 정적 지역 변수
        return instance;
    }

    void showMessage() {
        std::cout << "Hello from Meyers' Singleton!" << std::endl;
    }

private:
    // 생성자를 private으로 정의하여 외부에서 객체 생성을 막습니다.
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

int main() {
    Singleton& s1 = Singleton::getInstance();
    s1.showMessage();

    Singleton& s2 = Singleton::getInstance();
    s2.showMessage();

    // 두 참조가 동일한 인스턴스를 가리키는지 확인
    if (&s1 == &s2) {
        std::cout << "s1 and s2 are the same instance." << std::endl;
    }

    return 0;
}

 

이 예제에서 Singleton 클래스는 getInstance 메서드를 통해 정적 지역 변수로 유일한 인스턴스를 제공합니다. 이 방법은 초기화 시점에 스레드 안전성을 보장합니다.


싱글톤 패턴의 활용 사례

싱글톤 패턴은 다양한 상황에서 유용하게 사용될 수 있습니다. 몇 가지 일반적인 활용 사례를 소개하겠습니다.

  1. 로깅 클래스: 애플리케이션의 로그를 관리하는 클래스는 하나의 인스턴스만 있어야 합니다.
  2. 설정 클래스: 애플리케이션의 설정 정보를 관리하는 클래스는 전역에서 접근할 수 있어야 합니다.
  3. 스레드 풀: 리소스를 효율적으로 관리하기 위해 스레드 풀을 싱글톤으로 구현할 수 있습니다.

실습 문제

문제 1: 싱글톤 패턴을 사용하여 로깅 클래스 구현

싱글톤 패턴을 사용하여 로깅 클래스를 구현하고, 여러 번의 로그 메시지를 기록하는 프로그램을 작성하세요.

 

해설:

#include <iostream>
#include <mutex>
#include <fstream>

class Logger {
private:
    static Logger* instance;
    static std::mutex mtx;
    std::ofstream logFile;

    Logger() {
        logFile.open("log.txt", std::ios::app);
    }

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

    static Logger* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {
                instance = new Logger();
            }
        }
        return instance;
    }

    void logMessage(const std::string& message) {
        std::lock_guard<std::mutex> lock(mtx);
        logFile << message << std::endl;
    }

    ~Logger() {
        if (logFile.is_open()) {
            logFile.close();
        }
    }
};

Logger* Logger::instance = nullptr;
std::mutex Logger::mtx;

int main() {
    Logger* logger = Logger::getInstance();
    logger->logMessage("This is the first log message.");
    logger->logMessage("This is the second log message.");

    return 0;
}

 

문제 2: 마이어스의 싱글톤을 사용하여 설정 클래스를 구현

마이어스의 싱글톤을 사용하여 애플리케이션 설정 정보를 관리하는 설정 클래스를 구현하세요.

 

해설:

#include <iostream>
#include <string>

class Config {
private:
    std::string setting;

    Config() : setting("default") {}

public:
    Config(const Config&) = delete;
    Config& operator=(const Config&) = delete;

    static Config& getInstance() {
        static Config instance;
        return instance;
    }

    void setSetting(const std::string& newSetting) {
        setting = newSetting;
    }

    std::string getSetting() const {
        return setting;
    }
};

int main() {
    Config& config = Config::getInstance();
    std::cout << "Initial setting: " << config.getSetting() << std::endl;

    config.setSetting("custom setting");
    std::cout << "Updated setting: " << config.getSetting() << std::endl;

    return 0;
}

 

이제 8일차의 학습을 마쳤습니다. 싱글톤 패턴의 다양한 구현 방법과 활용 사례에 대해 상세히 학습하고 실습 문제를 풀어보았습니다.

 

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "팩토리 패턴"에 대해 학습하겠습니다.

반응형