싱글톤 패턴(Singleton Pattern)

참고) 싱글톤의 종류가 여러가지이다. 최하단에 템플릿 다이나믹 싱글톤만 보면 될 것



* 싱글톤 패턴 개념
- 가장 기본적인 디자인 패턴(32가지 패턴 중 편리하지만 많은 문제를 가짐)
- 오직 한 개의 클래스 인스턴스만을 갖도록 제한(인스턴스가 여러개일때 제대로 작동하지 않는 경우가 종종 있다)
- 전역적인 접근점을 제공
- 싱글톤 클래스는 맨 처음 생성된 이후로 프로그램 종료까지 단 한번 생성(메모리 누수 문제 발생이 낮다)



* 싱글톤 사용 이유(핵심: 한 개의 인스턴스 + 전역 접근)
1) 번거로운 인수 없이 전역에서 접근 가능
2) 한 번도 사용하지 않으면 인스턴스를 생성하지 않는다.
3) 늦은 초기화로 런타임에 초기화 가능
4) 싱글톤을 상속할 수 있다.



* 싱글톤의 문제: 클래스로 캡슐화된 전역 변수 이다. 즉, 전역 상태의 문제를 해결 할 수 없다.
* 전역 변수의 문제
1) 코드를 이해하기 어렵게 한다(소스코드 전역에서 찾아 살펴 봐야 하기 때문)
2) 커플링을 조장(인스턴스에 대한 접근을 통제하여 커플링을 제한할 수 있다)
3) 멀티스레딩 같은 동시성 프로그래밍에 맞지 않다.
    (전역변수 => 모든 스레드가 볼 수 있다 => 다른 스레드가 전역 데이터에 하는 작업을 모를 수 있다 = > 스레드 동기화 버그 생성)
4) 늦은 초기화(게으른 초기화)는 제어할 수 없다.(전투도중 초기화로 프레임 드랍 발생)



* 싱글톤 사용시 생기는 문제에 대한 예시(핵심: 한 개의 인스턴스 + 전역 접근)
1) 게임내 모듈 진단 정보를 Log로 남기고 싶다.
2) 모든 함수에 Log클래스 인스턴스를 추가하면 코드가 난잡해진다.
3) 이를 해결하는 가장 간단한 해결책이 Log클래스를 싱글톤으로 만들어 직업 Log클래스에 접근해 인스턴스를 얻게 할 수 있다.
4) 단, Log 객체를 하나 밖에 못만드는 제한이 생긴다.
5) 각 개발팀이 필요한 로그를 남기려 할때 로그 파일이 뒤죽박죽 섞여 버린다.
6) 전역 접근의 장점이 한 개의 인스턴스 생성이라는 단점을 발생시켰다.









* Basic Singleton(기본 싱글톤)
< 단점 >
1) static 클래스 멤버 변수는 static 전역 변수처럼 main함수 호출 이전에 초기화 되어 사용 여부에 상관 없이 메모리를 사용한다.
2) 정적개체이므로 다른 전역 객체 생성자에서 참조하려는 경우 문제 발생
class Singleton
{
private:
    Singleton(){};
    Singleton(const Singleton& other);
    static Singleton instance;
public:
    static Singleton* GetInstance()
    {
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;
//1< 사용법
Singleton::GetInstance()





* Dynamic Singleton(다이나믹 싱글톤)
- 기본 싱글톤의 문제를 늦은 초기화(게으른 초기화)를 사용하여 피한 것
- 다이나믹 싱글콘은 최초 GetInstance()를 호출하는 시점에 생성 된다.
- new로 동적 메모리 할당을 하였고, 해제를 위에 atexit 함수나, 다른 전역 객체의 소멸자 이용
class DynamicSingleton
{
private:
    DynamicSingleton() {};
    DynamicSingleton(const DynamicSingleton& other);
    ~DynamicSingleton() {};
    static DynamicSingleton* instance;
public:
    static DynamicSingleton* GetInstance()
    {
        if(instance == NULL) instance = new DynamicSingleton();
        return instance;
    }
};
DynamicSingleton* DynamicSingleton::instance = nullptr;
//!< 사용법
 DynamicSingleton::GetInstance()





* static Local Singleton(스테틱 지역 싱글톤)
- static 변수를 지역으로 사용한 싱글톤
- 해당 함수를 호출하는 시점에 초기화와 생성(객체를 사용하지 않으면 생성 x)
- 생성되면 static객체이므로 프로그램 종료까지 남아 있다 종료 시 소멸

//1. 첫번째 방식(포인터 없이 주소값 받기)
class LocalStaticSingleton
{
private:
    LocalStaticSingleton();
    LocalStaticSingleton(const LocalStaticSingleton& other);
    ~LocalStaticSingleton();
public:
    static LocalStaticSingleton& GetInstance()
    {
        static LocalStaticSingleton ins;
        return ins;
    }
};
//2. 두번째 방식(포인터 사용)
class LocalStaticSingleton
{
private:
    LocalStaticSingleton();
    LocalStaticSingleton(const LocalStaticSingleton& other);
    ~LocalStaticSingleton();
public:
    static LocalStaticSingleton* GetInstance()
    {
        static LocalStaticSingleton ins;
        return &ins;
    }
};





* Phoenix Singleton(피닉스 싱글톤)
- 싱글톤 참조시 해당 객체가 소멸되었다면 다시 되살림
- 헤더 <new><stdlib.h>를 추가해야함
- 강력한 기능을 가지지만 잘 쓰지 않는다
class PhoenixSingleton
{
private:
    static bool bDestroyed;
    static PhoenixSingleton* pIns;
    PhoenixSingleton() {};
    PhoenixSingleton(const PhoenixSingleton& other);
    ~PhoenixSingleton()
    {
        bDestroyed = true;
    }
    static void Create()
    {
        static PhoenixSingleton ins;
        pIns = &ins;
    }
    static void KillPheonix()
    {
        pIns->~PhoenixSingleton();
    }
public:
    static PhoenixSingleton& GetInstance()
    {
        if (bDestroyed)
        {
            new(pIns) PhoenixSingleton;
            atexit(KillPheonix);
            bDestroyed = false;
        }
        else if (pIns == NULL)
        {
            Create();
        }
        return *pIns;
    }
};
//!< 사용 방법
bool PhoenixSingleton::bDestroyed = false;
PhoenixSingleton* PhoenixSingleton::pIns = NULL;






Template Singleton(템플릿 싱글톤) <= 가장 대세(게임 포폴 제작에 사용) *넘모조아!*
- 상속만 해주면 싱글톤 기능을 활용할 가능(상속 받은 모든 객체가 싱글톤 역할)
- 일일이 모든 코드에 싱글톤 소스를 쓰지 않아도 되는 편리
- 다이나믹 싱글톤과 합쳐 Template Dynamic Singleton 을 사용
template typename T >
class TemplateSingleton
{
protected:
    TemplateSingleton()
    {
             
    }
    virtual ~TemplateSingleton()
    {
    }
public:
    static T * GetInstance()
    {
        if (m_pInstance == NULL)
            m_pInstance = new T;
        return m_pInstance;
    };
    static void DestroyInstance()
    {
        if (m_pInstance)
        {
            delete m_pInstance;
            m_pInstance = NULL;
        }
    };
private:
    static T * m_pInstance;
};
template <typename T> T * TemplateSingleton<T>::m_pInstance = 0;
//!< 사용법
class CObject : public TemplateSingleton<cobject>
{
public:
    CObject();
    ~CObject();
};





댓글