디자인 패턴/GoF

[디자인 패턴] 생성 패턴 (5) 싱글톤 Singleton (2021.02.19 수정)

로파이 2021. 1. 25. 16:58

싱글톤 (단일체-Singleton) 패턴

 

1. 의도

  • 프로그램 상 인스턴스가 하나만 있음을 보장. 전역적인 접근이 가능하다.

2. 활용

  • 프로그램 전체에서 인스턴스가 하나만 있고 모든 사용자가 접근 가능하도록 만든다.

3. 참여자

 

생생 패턴 - 싱글톤

  • Singleton: Instance() 연산을 정의하여 유일한 인스턴스에 접근한다. Instance() 연산은 정적 멤버 함수로 구현하여 모든 사용자가 접근할 수 있도록 한다.

4. 협력 방법

  • 사용자는 instance() 연산을 이용하여 단일체 인스턴스에 접근할 수 있다.

5. 결과

  • 유일한 존재를 보장하고 인스턴스 접근을 통제한다.
  • 전역 변수로 선언을 피함으로 이름 공간 혼란을 줄일 수 있다.

6. 구현

 

예시 코드로 설명

  • Instance(): 단일체인 자신을 반환하는 접근자
  • Release(): 소멸자 역할을 대신
  • 사용자가 또 다른 인스턴스를 생성하는 것을 방지하기 위해 단일체에 대한 생성자를 protected로 보호한다.
class Singleton
{
public:
	static Singleton* Instance();
	void Release();
protected:
	Singleton(){}
	~Singleton(){}
private:
	static Singleton* _instance;
};

Singelton* Singleton::_instance = 0;

Singleton* Singleton::Instance()
{
	if (_instance == 0)
	{
		_instance = new Singleton;
	}
	return _instance;
}

void Singleton::Release()
{
	if (_instance)
	{
		delete _instance;
		_instance = 0;
	}
}
  • 싱글톤 패턴의 객체의 인스턴스화는 Instance() 호출 후 포인터를 체크하여 늦은 인스턴스화가 일어난다.
  • 다중 스레드에서 객체의 인스턴스화 시점에 대한 동기화 문제가 있다.

싱글톤 패턴은 전역적인 접근을 포인터로 구현하여 인스턴스를 접근하여 얻을 때 생성되는 것으로 많이 구현하는 것 같다.

포인터로 인스턴스를 관리하면 복사 생성과 대입 생성을 그나마 포인터 차원으로 축소 시켜 같은 유일한 인스턴스를 유지할 수 있는데, ->연산자를 이용하여 인스턴스를 접근하면 이는 타이핑에서 여러모로 귀찮지 않을까 해서 레퍼런스 타입의 싱글톤 패턴을 써보면 어떨까 해서 찾아보니 관련 구현이 있었다.

 

1. 레퍼런스 타입과 포인터의 똑같기 때문에 레퍼런스 타입으로 써보면 어떨 까라는 생각을 해본다.

 

2. 대신 복사 생성과 대입 생성 (이동 생성/대입)등을 삭제하여 유일한 인스턴스를 보장해야한다.

class Singleton
{
private:
  static Singleton _instance;
  Singleton();
  ~Singleton();
public:
  static Singleton& instance()
  {
  	return _instance;
  }
  Singleton(const Singleton&) = delete;
  Singleton(const Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(const Singleton&&) = delete;
};

Singleton::Singleton()
{
}

Singleton::~Singleton()
{
}

싱글톤 클래스가 동적 할당한 변수를 가진 경우가 아니라면 아직까지는 메모리 누수 없이 소멸자를 기본으로 사용해도 될 것 같다.

(+추가) 객체의 인스턴스에 대해 포인터로 관리하는 것이 경험적으로 더 좋은 것 같다.

관련 글

codereview.stackexchange.com/questions/197486/singleton-class-and-correct-way-to-access-it-in-c

 

 

- 전역변수 선언을 잊지말고 cpp에 해주어야 링크 에러가 나지 않는다. 

// Singleton.cpp
Singleton Singleton::INSTANCE;

싱글톤 패턴을 위한 매크로 버전

#define DEFINITION_SINGLE(Type) Type* Type::_instance = nullptr;
#define DECLARE_SINGLE(Type)	\
		private:\
			static Type * _instance;\
			Type();\
			~Type();\
		public:\
			static Type* Instance()\
			{\
				if(_instance == 0)\
				{\
					_instance = new Type;\
				}\
				return _instance;\
			}\
			void Release()\
			{\
				if(_instance)\
				{\
					delete _instance;\
					_instance = nullptr;\
				}\
			}\
			Type(const Type&) = delete;\
			Type(const Type&&) = delete;\
			Type& operator=(const Type&) = delete;\
			Type& operator=(const Type&&) = delete;