BLOCK

싱글턴 패턴(Singleton) 본문

BLOCK::MATERIAL/BLOCK::CODE

싱글턴 패턴(Singleton)

BLOCK 2025. 2. 23. 05:12
728x90

싱글턴 패턴(Singleton)

싱글턴은 클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근(액세스) 지점을 제공하는 생성 디자인 패턴입니다.

 

1. 싱글턴 패턴(Singleton)

싱글턴 주요 키워드

  1. 전역적인 접근을 위한 정적변수사용
  2. 생성자보호를 위해서 생성자를 private 으로 설정한다.
  3. 복사생성자와 복사대입연산자를 막아서 단일 인스턴스 원칙을 고수합니다.

1-1) 동적 할당 방식

싱글턴 객체의 instance를 생성할때 new연산자를 이용해서 생성하는 방식

class Singleton 
{
public:
    static Singleton* GetInstance() 
    {
        if (instance == nullptr) 
        {
            instance = new Singleton();  // 동적 할당
          }
        return instance;
    }

    static void DestroyInstance() // 동적할당 객체는 수동으로 삭제 필요
    {  
        delete instance;
        instance = nullptr;
    }

    Singleton(const Singleton&) = delete; // 복사생성자 삭제
    Singleton& operator=(const Singleton&) = delete; // 복사대입 연산자 삭제

private:
    Singleton() { std::cout << "make ton" << std::endl; }
    ~Singleton() { std::cout << "delete ton" << std::endl; }

        static Singleton* instance; // 전역적인 접근을 위한 정적 멤버변수
};
Singleton* Singleton::instance = nullptr; // 정적변수의 초기화는 클래스 외부에서 해준다

장점

  • 소멸시점이 자유롭습니다.

단점

  • delete 를 잊으면 메모리누수의 위험이 있습니다.

1-2) 정적 지역 변수 방식

싱글턴 객체를 생성할때 GetInstance() 함수안에서 정적지역변수 (static local variable)를 직접 생성하는 방식

class Singleton
{
public:
    static Singleton& GetInstance()
    {
        static Singleton instance;  // 프로그램 종료 시 자동 소멸
        return instance;
    }

    Singleton(const Singleton&) = delete; // 복사생성자 삭제
    Singleton& operator=(const Singleton&) = delete; // 복사대입 연산자 삭제

private:
    Singleton() { std::cout << "make ton" << std::endl; }
    ~Singleton() { std::cout << "delete ton" << std::endl; }

};

C++11 이전에는 정적 지역 변수를 초기화하는 과정에서 멀티스레드 환경에서 경쟁 조건(Race Condition) 이 발생할 수 있었으나

C++11에서는 함수 내부에 정의된 정적 변수의 초기화가 스레드로부터 안전하도록 보장되어 정적 지역 변수 방식을 사용할 수 있게 되었습니다.(이 기능을 비공식적으로 Magic Static이라고 부르고 있습니다.)

장점

  • 지역변수이기 때문에 프로그램 종료시 자동으로 소멸하여 메모리관리에 용이합니다.
  • 메모리누수 위험이 없습니다.
  • 동적할당 과정이 없어 속도에 유리합니다.
  • C++11 이후를 기준으로 스레드 안정성을 보장합니다.

단점

  • 소멸시점이 프로그렘 종료시로 고정됩니다.

1-3) CRTP를 이용한 방식

싱글턴의 형태를 갖춘 템플릿을 만들어두고 상속을 통해 다양한 클래스에 쉽게 싱글턴 패턴을 적용시키는 방식

파생클레스에서 내부멤버에 접근 할 수 있도록 하기위해 template에서 생성자를 protected 로 지정해줍니다.

CRTPBase에서 파생클래스의 생성자에 접근할 수 있도록 friend class선언을 해줍니다.

이때 protected 지정되어있는 GetInstance() 함수에 접근하기 위해 public상속을 해주어야 합니다.

template<typename T>
class SingletonBase
{
public:
    static T& GetInstance() 
    {
        static T instance; // 정적 지역 변수로 단일 인스턴스 보장
        return instance;
    }

protected:
        SingletonBase() = default;
    ~SingletonBase() = default;

    // 복사와 대입 방지
        SingletonBase(const SingletonBase&) = delete;
    SingletonBase& operator=(const SingletonBase&) = delete;

}; 
class MyClass: public SingletonBase<MyClass>
{
public:
    void func(){/*필요한 함수구현*/}

private:
    MyClass() {}
    ~MyClass() {}
    // 싱글턴 private 생성자에 접근 가능하도록 friend 선언
    friend class SingletonBase<MyClass>;
};

장점

  • 컴파일 타임 정적 다형성 제공
  • 인라인 최적화 가능
  • 가상 함수 오버헤드 제거
  • 코드 재사용성 및 확장성 향상.

단점

  • 템플릿 복잡성 증가
  • 난해한 컴파일 에러 메시지
  • 빌드 시간 증가
  • 디버깅 어려움

'BLOCK::MATERIAL > BLOCK::CODE' 카테고리의 다른 글

고도엔진 VisualStudio로 실행하기  (0) 2025.03.02
Comments