무분별한 객체생성을 방지하고, 1개의 객체만 생성하여 이용하는 프로그램 코딩에 유용하게 적용할 수 있다.


일반적인 싱글톤 패턴의 코드는 아래와 같다.


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
class Singleton
{
private:
  Singleton() {}
  ~Singleton() {}
  Singleton(const Singleton& s) {}
 
public:
  static Singleton* GetInstance()
  {
    if (m_pInstance == NULL)
    {
      m_pInstance = new Singleton;
    }
 
    return m_pInstance;
  }
 
private:
  static Singleton* m_pInstance;
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
  Singleton* pSingleton = Singleton::GetInstance();
 
  return 0;
}


문제가 없는듯 보이는데 그러나 이 패턴에는 문제점이 존재한다. 바로 new로 생성한 객체의 소멸을 보장받지 못하는 것이다. (리소스 누수현상 발생)

이러한 누수현상에 대처하기 위해서 동적 생성이 아닌 정적 생성 사용을 예로 들 수 있다.

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
class Singleton
{
private:
  Singleton() {}
  ~Singleton() {}
  Singleton(const Singleton& s) {}
 
public:
  static Singleton* GetInstance()
  {
    return &m_pInstance;
  }
 
private:
  static Singleton m_pInstance;
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
  Singleton* pSingleton = Singleton::GetInstance();
 
  return 0;
}

정적 객체를 생성하여 프로그램 종료시 객체 반환할 필요가 없어졌다.


처음으로 돌아가서 동적 생성을 통한 싱글턴 패턴 인스턴스 소멸은 어떻게 가능할 수 있는지 확인해보도록 하자.

atexit() 함수에 대하여

atexit 함수는 프로그램 종료시에 (main함수 종료 직전) 실행되며, 인자는 함수 포인터를 가진다.

int atexit(

   void (__cdecl *func )( void )

);

이 정의에서 알수 있듯이 atexit 함수는 인수를 취할 수 없고, 리턴값도 없는 void func(void)형이어야 한다. 또한 이 함수는 32개까지 등록될 수 있으며 최후로 등록된 함수가 가장 먼저 실행된다.

atexit함수를 이용하면 프로그램 종료 직전에 내가 원하는 작업을 할 수 있는 기능을 만들어 줄 수가 있게 된다. 


아래 코드를 보자.

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
#include "stdafx.h"
 
class Singleton
{
private:
  Singleton() {}
  ~Singleton() {}
  Singleton(const Singleton& s) {}
 
private:
  static void destroy() { delete m_pInstance; }  // 객체 소멸
 
public:
  static Singleton* GetInstance()
  {
    if (m_pInstance == NULL)
    {
      m_pInstance = new Singleton();
      atexit(destroy);    // 프로그램 종료 직전호출
    }
 
    return m_pInstance;
  }
 
private:
  static Singleton* m_pInstance;
};
 
Singleton* Singleton::m_pInstance = NULL;
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
  Singleton* pSingleton = Singleton::GetInstance();
 
  return 0;
}


자 위 코드는 동적 객체를 생성하고 atexit()함수를 이용하여 main()함수 종료 전에 객체를 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
#include "stdafx.h"
 
//------------------------------------------------------------------
// template singleton pattern
template<typename T>
class Singleton
{
protected:
  Singleton() {}
  virtual ~Singleton() {}
  Singleton(const Singleton& s) {}
 
private:
  static void destroy() { delete m_pInstance; }  // 객체 소멸
 
public:
  static T* GetInstance()
  {
    if (m_pInstance == NULL)
    {
      m_pInstance = new T();
      atexit(destroy);    // 프로그램 종료 직전호출
    }
 
    return m_pInstance;
  }
 
private:
  static T* m_pInstance;
};
 
template <typename T> T* Singleton <t>::m_pInstance;
 
//------------------------------------------------------------------
// test code
 
class 테스트 : public Singleton<테스트>
{
public:
  void 싱글턴_테스트() { cout << "템플릿 싱글턴 테스트 합니다." << endl; }
};
 
//------------------------------------------------------------------
// Main
int _tmain(int argc, _TCHAR* argv[])
{
  테스트::GetInstance()->싱글턴_테스트();
 
  return 0;
}




멀티 스레드 기반에서 싱글턴 패턴 구현 (volatile 활용)

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
template<typename T>
class CSingleton : public CMultiThreadSync<T>
{
protected:
  CSingleton() {}
  virtual ~CSingleton() {}
  CSingleton(const CSingleton& s) {}
 
private:
  static void destroy() { delete m_pInstance; }  // 객체 소멸
 
public:
  static T* GetInstance()
  {
    if (m_pInstance == NULL)
    {
      CThreadSync cs;
 
      m_pInstance = new T();
      atexit(destroy);    // 프로그램 종료 직전호출
    }
 
    return m_pInstance;
  }
 
private:
  static T* volatile m_pInstance;
};
 
template <typename T> T* volatile CSingleton <T>::m_pInstance;




스마트 포인터 shared_prt<> 을 활용한 싱글턴 패턴

스마트 포인터와 shared_ptr 이란?

  • 스마트 포인터 : heap 메모리에 new, malloc으로 할당된 경우 자동으로 free, delete(소멸) 해주는 똑똑한 포인터 (auto_ptr, shared_ptr, weak_ptr 등이 있다)
  • shared_ptr : 참조 횟수(reference counting)에 따른 메모리 할당 해제를 수행한다. 즉, 최초 객체를 할당시 실질적으로 메모리에서 할당받고, 그 다음부터는 객체 복사시 실제 복사는 이루어지지 않고 카운팅을 올려서 관리하는 기법이다. 또한 카운팅이 0가 되었을 경우 메모리를 자동 해제하게 된다.


shared_ptr<자료형> 이름(사이즈);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T>
class CSingleton
{
public:
  static T* GetInstance()
  {
    std::call_once(m_onceFlag, [] {
      m_pInstance.reset(new T);
    });
 
    return m_pInstance.get();
  }
 
private:
  static std::shared_ptr<T> m_pInstance;
  static std::once_flag m_onceFlag;
};
template<typename T> std::shared_ptr<T> CSingleton<T>::m_pInstance = NULL;
template<typename T> std::once_flag CSingleton<T>::m_onceFlag;


우리는 위에서 atexit() 함수를 활용하여 소멸자 역할을 할 수 있는 함수를 프로그램 종료직전에 명시적으로 호출하도록 하였다. 하지만 이 기능은 뭔가 꺼림찍하고 깔끔해보이지 않는다. 그래서 shared_ptr 스마트포인터를 활용하여 새로운 싱글턴 패턴을 구현하였다.

'CPP' 카테고리의 다른 글

STL 정렬  (0) 2015.12.23
STL 기본  (0) 2015.12.23
모듈 이 safeseh 이미지 에 대해 안전 하지 않습니다  (0) 2014.10.01
싱글톤 2  (0) 2014.09.30
Posted by wakira
,