티스토리 뷰

공부/디자인 패턴

싱글톤 패턴

-=HaeJuK=- 2024. 3. 4. 21:33
728x90

싱글턴(Singleton) 패턴은 객체의 인스턴스가 애플리케이션 내에서 단 하나만 생성되도록 보장하는 디자인 패턴입니다. 이 패턴은 전역 변수를 사용하지 않고 객체에 대한 전역 접근을 제공하며, 동시에 여러 인스턴스의 생성을 방지하여 리소스 사용을 최적화할 수 있습니다.

  1. 생성자를 비공개로 만들어 클래스 외부에서 인스턴스를 직접 생성할 수 없게 합니다.
  2. 클래스 내에 정적(private static) 멤버 변수로 자기 자신의 유일한 인스턴스를 저장합니다.
  3. 공개적인 정적 메서드(getInstance) 를 제공하여 유일한 인스턴스에 접근할 수 있게 합니다. 이 메서드는 내부적으로 인스턴스가 존재하지 않을 경우에만 생성하고, 이미 존재하는 경우에는 기존 인스턴스를 반환합니다.

 

#include <iostream>

class Singleton {
private:
    // 1. 생성자, 복사 생성자, 대입 연산자를 비공개로 선언합니다.
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 2. 유일한 인스턴스를 저장할 정적 멤버 변수를 선언합니다.
    static Singleton* instance;

public:
    // 3. 유일한 인스턴스에 접근할 수 있는 정적 메서드를 제공합니다.
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    void doSomething() {
        // 싱글턴 인스턴스가 할 작업을 정의합니다.
        std::cout << "Doing something" << std::endl;
    }
};

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

int main() {
    // 싱글턴 인스턴스에 접근하여 메서드를 호출합니다.
    Singleton::getInstance()->doSomething();

    return 0;
}

 

C++에서 싱글턴 패턴을 스레드 안전하게 구현하는 방법은 몇 가지가 있습니다. 여기서는 가장 일반적으로 사용되는 몇 가지 방법을 소개하겠습니다.

1. 더블 체킹 록(Double-Checked Locking) 패턴

더블 체킹 록 패턴은 싱글턴 인스턴스가 아직 생성되지 않았을 때만 잠금을 사용하여 인스턴스를 생성하는 방법입니다. 이 방법은 인스턴스가 이미 생성된 후에는 잠금 오버헤드 없이 접근할 수 있어 효율적입니다. 하지만, C++에서는 메모리 모델의 복잡성으로 인해 이 패턴을 올바르게 구현하는 것이 어렵습니다. C++11 이후의 메모리 모델에서는 아래와 같이 std::atomic과 std::call_once를 사용하여 이 패턴을 안전하게 구현할 수 있습니다.

2. std::call_once와 std::once_flag 사용

C++11에서는 스레드 안전한 초기화를 위해 std::call_once와 std::once_flag를 제공합니다. 이 방법을 사용하면 초기화 코드가 단 한 번만 실행되도록 보장되며, 여러 스레드에서 동시에 접근하더라도 안전합니다.

#include <iostream>
#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        std::call_once(initInstanceFlag, &Singleton::initSingleton);
        return *instance;
    }

    static void initSingleton() {
        instance = new Singleton();
    }

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

private:
    Singleton() {}
    static Singleton* instance;
    static std::once_flag initInstanceFlag;
};

Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::initInstanceFlag;

int main() {
    // 다중 스레드 환경에서도 안전하게 싱글턴 인스턴스에 접근
    Singleton& singletonInstance = Singleton::getInstance();
    return 0;
}

 

3. static 지역 변수 사용 (Meyers' Singleton)

Scott Meyers에 의해 소개된 이 방법은 C++11 이후 가장 권장되는 방식 중 하나입니다. 함수 내부의 static 지역 변수를 사용하면, 첫 호출 시에만 초기화되고 스레드 안전성이 보장됩니다. C++11 표준에서는 이러한 초기화가 스레드 안전하도록 명시하고 있습니다.

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }

private:
    Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함
250x250