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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /****************************************************************************** 파일명 : Singleton.h 목적 : 싱글턴 패턴 생성 사용방식 : public 상속을 받아 사용 ******************************************************************************/ #pragma once template<class T> class Singleton { private: static T* m_pInstatnce; protected: Singleton(const Singleton&) = delete; Singleton(const Singleton&&) = delete; Singleton& operator = (const Singleton&) = delete; Singleton& operator = (const Singleton&&) = delete; Singleton() { } virtual ~Singleton() { } public: /**************************************************************************** 함수명 : GetInstance 설명 : T 클래스의 인스턴스 주소를 리턴받음 *****************************************************************************/ static T* GetInstance() { if (m_pInstatnce == nullptr) { m_pInstatnce = new T; } return m_pInstatnce; } /**************************************************************************** 함수명 : DestroyInstance 설명 : 인스턴스를 지워줌 *****************************************************************************/ void DestroyInstance() { SAFE_DELETE(m_pInstatnce); } }; //m_pInstatnce를 nullptr로 초기화 template<class T> T* Singleton<T>::m_pInstatnce = nullptr; | cs |
이와 같은 Singleton 을 사용 할때
1 2 3 4 5 6 7 8 9 10 | class temp : public Singleton<temp> { public: temp() { }; int a = 3; }; | cs |
요로코롬 상속 받아서 사용하고 있었다.
그런데 이렇게 template Singleton을 사용할 때의 문제점은
main(){
temp a;
}
이렇게 생성해도 아무런 오류가 나오지 않는다는 것이다.. 즉 여러 개 만들 수도 있다는 것.
하나의 객체만 가지고 싶어서 만드는 Singleton 이였는데... 뭔가 어긋나는 느낌이 든다.
그래서
1 2 3 4 5 6 7 8 9 | class temp : public Singleton<temp> { temp() {}; public: int a = 3; }; | cs |
이렇게 생성자를 private로 바꿔서 만들게 되면 main 이나 다른 곳에서 temp를 만드는 것은 불가능하게 된다.
하지만..
1 2 3 4 | if (m_pInstatnce == nullptr) { m_pInstatnce = new T; } | cs |
그렇게 되면 이렇게 객체를 동적할당 할 때에 new에서 오류가 난다.
new 는 생성자를 부르기 때문에 private에 선언된 생성자를 부를 수 없기 때문에 오류가 난다.
그렇기 때문에 생성자를 호출하지 않는 malloc 을 사용한다면..
1 2 3 4 5 | if (m_pInstance == NULL) { m_pInstance = (T*)malloc(sizeof(T)); // m_pInstance = new T; memset(m_pInstance, NULL, sizeof(*m_pInstance)); } | cs |
된다...
하지만 뭔가 만약에 T 객체내부에서 생성자와 같은 것으로 초기화 되기이전에 값을 넣었놨다면 그것마저 0으로 초기화가 되는 현상이 있었다.
때문에 Singleton은
1 2 3 4 5 | if (m_pInstatnce == nullptr) { m_pInstatnce = new T; } | cs |
그냥 이렇게 가져가고 상속 받는 클래스에서 friend로 Singleton을 보게해주자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class temp : public Singleton<temp> { friend Singleton; temp() { }; public: // void* operator new(std::size_t) = delete; int a = 3; }; | cs |
그렇게 되니 무리가 없다... 끝!!
최종
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | template<class T> class Singleton { private: static T* m_pInstatnce; protected: Singleton(const Singleton&) = delete; Singleton(const Singleton&&) = delete; Singleton& operator = (const Singleton&) = delete; Singleton& operator = (const Singleton&&) = delete; Singleton() { } virtual ~Singleton() { } public: /**************************************************************************** 함수명 : GetInstance 설명 : T 클래스의 인스턴스 주소를 리턴받음 *****************************************************************************/ static T* GetInstance() { if (m_pInstatnce == nullptr) { m_pInstatnce = new T; } return m_pInstatnce; } /**************************************************************************** 함수명 : DestroyInstance 설명 : 인스턴스를 지워줌 *****************************************************************************/ void DestroyInstance() { SAFE_DELETE(m_pInstatnce); } }; //m_pInstatnce를 nullptr로 초기화 template<class T> T* Singleton<T>::m_pInstatnce = nullptr; class temp : public Singleton<temp> { friend Singleton; temp() { }; public: }; | cs |
static pointer에 new를 통해 동적할당하는 것이 ThreadSafe 할지 안할지 찾아봤었는데.
c++ 11 이후에서 부터는 굳이 정적변수초기화 앞뒤로 Lock 을 걸어주지 않아도 ThreadSafe 하다고 한다.
때문에 new는 별로 안전하지 않은 것 같다..
어차피 static 으로 선언한 변수는 여러번 선언해봤자 같은 메모리를 사용하므로 그냥
1 2 3 4 5 | static T& getInstance() { static T instance; return instance; } | cs |
이렇게 사용하는게 좋겠다.
'서버프로그래밍' 카테고리의 다른 글
객체 초기화에 memset을 이용한 방법의 위험성.. (0) | 2018.11.03 |
---|---|
c++ thread 를 사용하기 위한 방법들 나와있는 페이지 (0) | 2018.11.02 |
짬내서 만드는 IOCP 채팅과제 - 1 (0) | 2018.10.30 |
PostThreadMessage의 ThreadId (0) | 2018.08.27 |
ZeroByteRecv - PageLocking 관련 (0) | 2018.08.22 |