쉬는 날이라 그런지 이상하게 더 하고 싶어진다.

뇌내에 청개구리가 들었다.

 

요새 포스팅 올라오는 게 뭐 이렇게 두서가 없냐면 현재 프로젝트를 하고 있기 때문이다.

써먹을 걸 차곡차곡 쌓는 중이다.

사실 프로젝트라 할만한지도 모르겠다. 시간을 많이 투자를 안했다. 

초기라 뭘 어떻게 할지 머리가 많이 아프다.

 

잡담 그만하고 시작하겠다.

싱글톤 패턴 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<intboolint>
    public Singleton <print_t>
{
public:
    int operator()(bool var1, int var2)
    {
        std::cout << var1 << var2;
    }
};
 
int main()
{
    auto& print = *(print_t::instance());
    print(true3);
    print(false1);
    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 포럼

 

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<intintint>
    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

+ Recent posts