쉬는 날이라 그런지 이상하게 더 하고 싶어진다.
뇌내에 청개구리가 들었다.
요새 포스팅 올라오는 게 뭐 이렇게 두서가 없냐면 현재 프로젝트를 하고 있기 때문이다.
써먹을 걸 차곡차곡 쌓는 중이다.
사실 프로젝트라 할만한지도 모르겠다. 시간을 많이 투자를 안했다.
초기라 뭘 어떻게 할지 머리가 많이 아프다.
잡담 그만하고 시작하겠다.
싱글톤 패턴 C++로 구현하기(Singleton pattern) (tistory.com)
싱글톤 패턴 C++로 구현하기(Singleton pattern)
싱글톤 패턴이란? 싱글톤 패턴은 아무리 많은 객체를 생성해도, 단 하나의 인스턴스만을 생성한 것과 같은 디자인 패턴이다. 원래 같으면 여러 개의 객체를 생성하면 각각의 객체가 가진 변수는
everydaywoogi.tistory.com
Singleton 클래스는 밖에서 자신의 인스턴스를 할당하지 못하도록 생성자를 private으로 가진다.
즉 Singleton 밖의 함수는 Singleton의 인스턴스를 자체적으로 할당하지 못하고 Singleton 클래스를 통해야 한다.
위 블로그의 예제에서는 GetInstance() 메서드가 Singleton 클래스 인스턴스를 반환해주는 역할을 한다.
근데 위의 예제는 개선이 필요해보이기도 한다.
잘 모르는 나의 직관에 따르자면 위의 예제는 GetInstance()가 클래스를 생성하고 얻는 역할을 동시에 하고 있다.
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 | #include<iostream> class Singleton { private: static Singleton* instance; Singleton(const Singleton& other); Singleton() { value = 5; }; ~Singleton() {}; int value; static void MakeInstance(){ instance = new Singleton(); } public: static Singleton* GetInstance() { if (not Singleton::instance) { Singleton::MakeInstance(); } return Singleton::instance; } int Get() { return this->value; } int Set(int par) { this->value = par; } }; Singleton* Singleton::instance = nullptr; int main() { Singleton* temp1 = Singleton::GetInstance(); Singleton* temp2 = Singleton::GetInstance(); temp1->Set(3); std::cout << temp1->Get() << std::endl; temp2->Set(4); std::cout << temp2->Get() << std::endl; return 0; } | cs |
그래서 위와 같이 바꿔주었다.
저거로 쓰려다가 더 쉽게 만드는 방법은 없을까 해서 구글에 검색해보았다.
정말 괜찮은 글을 발견할 수 있었다.
Generic Singleton and Functor Classes using C++ Templates | snesgaard (wordpress.com)
Generic Singleton and Functor Classes using C++ Templates
In this post I will be covering two specific classes which I will be using a lot in the future. Also I will be showing how C++ templates may be utilized in order to write with a very high reusabili…
snesgaard.wordpress.com
내용은 템플릿으로 Singleton 알아보기 쉽게 구현하는 법
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 | template <class T> class Singleton{ public: static T * instance(){ if(!pInstance){ pInstance = new T; } return pInstance; } static void freeInstance(){ if(pInstance){ delete pInstance; pInstance=NULL; } } protected: Singleton(){ pInstance = NULL; } ~Singleton(){ if(pInstance){ delete pInstance; } } static T * pInstance; }; template T* Singleton::pInstance=NULL; | cs |
1 2 3 4 5 6 7 8 9 10 11 12 | class Spam:public Singleton<Spam>{ public: //public methods and members goes here private: friend class Singleton<Spam>; Spam(); Spam( const Spam & other ); ~Spam(); }; | cs |
Functor도 가능
1 2 3 4 5 | template <class ReturnType,class ... Arguments> class Functor{ public: virtual ReturnType operator()( Arguments ... arguments ) = 0; }; | 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 | #include<iostream> template <class T> class Singleton { public: static T* instance() { if (not pInstance) { pInstance = new T; } return pInstance; } static void freeInstance() { if (pInstance exist) { delete pInstance; pInstance = nullptr; } } protected: Singleton() { pInstance = nullptr; } ~Singleton() { if (pInstance) { delete pInstance; } } static T* pInstance; }; template<typename T> T* Singleton<T>::pInstance = nullptr; template <class ReturnType, class ... Arguments> class Functor { public: virtual ReturnType operator()(Arguments ... arguments) = 0; }; class print_t: public Functor<int, bool, int>, public Singleton <print_t> { public: int operator()(bool var1, int var2) { std::cout << var1 << var2; } }; int main() { auto& print = *(print_t::instance()); print(true, 3); print(false, 1); print.freeInstance(); return 0; } | cs |
요래썼더니 깔끔해졌다.
레퍼런스가 짱이다.
음 위 코드로부터 stack overflow 오류가 발생하였다.
디버거 돌려보니 이 부분이었다.
1 2 3 4 5 | virtual ~Singleton() { if (pInstance != nullptr) { delete pInstance; } } | cs |
뭔가 하고 한참 생각해보았다.
정답은 소멸자가 무한재귀를 하는 것이었다.
그래서 재귀함수 재는 카운터를 재고 해봤는데
이번엔 더블프리 버그가 떴다.
1 2 3 4 5 6 7 8 9 10 11 | bool SingletonDestructor_Recursiving = false; virtual ~Singleton() { if (pInstance != nullptr) { if (not SingletonDestructor_Recursiving) { SingletonDestructor_Recursiving = true; delete pInstance; } } } | cs |
그래서 c++ static 상속에 관해서 자세히 검색해보았다.
static 멤버 상속에 대해 자세한 내용을 모르니 디버깅하는 게 힘들었다.
static 멤버의 상속 - GpgStudy 포럼
프로그래밍 일반에 관한 포럼입니다. 운영자: 류광 mika 전체글: 537 가입일: 2005-01-17 22:42 전체글 글쓴이: mika » 2006-10-23 17:06 자식 클래스마다 고유한 static 값이 필요해서 static 변수와 static 함수를
www.gpgstudy.com
static 멤버는 상속되어도 단 하나만 존재한다고 한다.
근데 자세히 생각해보니 SingletonDestructor_Recursiving 변수가 제대로 대입되지 않았다.
1 2 3 4 5 6 7 8 9 10 | virtual ~Singleton() { if (pInstance != nullptr) { SingletonDestructor_Recursiving = true; if (not SingletonDestructor_Recursiving) { delete pInstance; } } } | cs |
드디어 컴파일이 되었다.
오전 8:03 -------
나의 포스팅을 다시 보았다.
그리고 내가 뭐했나 생각했다.
저건 수정시킨 게 아니라 delete 무효화시킨건데.
그래서 이전대로 소스복붙하고 다시 디버그 시작하려고 했다.
무엇이 문제였을까.
답은 드러나지 않았다.
왜냐하면 이번엔 멀쩡히 작동했기 때문이다.(???)
뭐였을까
--------------------
다음은 최종 코드
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 62 63 | #include<iostream> template <class T> class Singleton { public: static T* instance() { if (not pInstance) { pInstance = new T; } return pInstance; } static void freeInstance() { if (pInstance) { delete pInstance; pInstance = nullptr; } } protected: Singleton() { pInstance = nullptr; } bool SingletonDestructor_Recursiving = false; virtual ~Singleton() { if (pInstance != nullptr) { SingletonDestructor_Recursiving = true; if (not SingletonDestructor_Recursiving) { delete pInstance; } } } static T* pInstance; }; template<typename T> T* Singleton<T>::pInstance = nullptr; template <class ReturnType, class ... Arguments> class Functor { public: virtual ReturnType operator()(Arguments ... arguments) = 0; }; class print_t: public Functor<int, int, int>, public Singleton <print_t> { private: friend class Singleton<print_t>; public: int operator()(int var1, int var2) { std::cout << var1 * var2; return 0; } }; int main() { return 0; } | cs |
오전 8:16 -------
코드를 다시 보았는데
Recursiving 변수는 그냥 private으로 두면 되는 걸 왜 저렇게 했을까
지금은 수정되었다.
---------------------
코드를 이리저리 만져보다가 무언가 중대한 결함을 발견했다.
Singleton 클래스에서 복사생성자가 막혀있지 않다는 것이었다.
근데 Functor는 보통은 레퍼런스로 넘기지만 가끔씩 복사될 때도 있다.
즉 복사생성자를 막아놓으면 쓸 곳에 못쓸 수도 있다는 것.
복사될 때는 어떻게 쓰는지 생각해보았다.
클라이언트로 Functor를 넘겨받았지만
서버 내에서 체크해보니 상태가 이상해서 그 Functor를 바꿔서 사용할 수 있다.
결론적으로 내가 여태껏 했던 것들은 쓰잘데기 없는 것이었다.
Functor을 애초에 왜 Singleton으로 쓰고 싶어했던 것일까부터 올라가며 생각해보았다.
이유는 없었다.
나는 왜 Singleton과 Functor을 결합하려고 했을까?
그냥 있는대로 써도 되는데 말이다.
일단 Singleton 클래스는 제 역할을 해야하므로 복사생성자를 없애는 개조를 하려고 했다.
그래서 Singleton(const Singleton& par) = delete;
라고 선언해놓으니까
int형으로 명시되어있지 않다는 이상한 에러가 뜬다.
검색해보니 템플릿만의 복사생성자 만드는 방법이 있다는 것 같다.
진행하면서 차차 개조해놓을 것이다.
이러니까 디자인패턴 책을 c++을 따로 사야된다는 거구나.
템플릿도 좀 공부해놓아야겠다.
월요일부턴 언리얼을 배우며 프로젝트할 것이다.
이렇게 동시에 진행하는 것은 바닥부터도 만들어보고 언리얼도 배워보고 하고 싶기 때문이다.
바닥부터 만들어보는 것도 많은 공부가 될 수 있을 것 같다.
'진행중인 프로젝트들' 카테고리의 다른 글
게임프로그래밍패턴-01 서론 (0) | 2023.01.15 |
---|---|
Bitmap (0) | 2023.01.15 |
키 입력 아이디어 조각 (2) | 2023.01.08 |
RPG 게임 프로젝트 시작 (0) | 2022.12.27 |
Simple Factory 예제 (0) | 2022.12.14 |