bdfgdfg

[온라인 서버] 네트워크 라이브러리 - Singleton Class 본문

게임프로그래밍/서버 책

[온라인 서버] 네트워크 라이브러리 - Singleton Class

marmelo12 2022. 1. 5. 13:30
반응형

Singleton class

- 많이 사용하는 디자인 패턴 기법으로 오직 하나의 객체만 생성하여 사용하는 클래스를 의미한다.

  -> 패턴은 쉽게 말하면 개발자들의 경험을 통한 문제 해결 방법들을 정리하여 이름을 부여한 기법들.

- 이러한 단일체(Singleton)클래스는 어떤 클래스(객체)들을 관리하는 역할로 많이 쓰인다.

  -> ex) User를 관리하는 UserManager등등.. 보통 Manager가 붙은 클래스들이 Singleton을 사용한다.

 

먼저 싱글톤 클래스를 하는데 싱글톤 패턴을 사용하는 모든 단일체 클래스는 이 클래스를 상속받는다.

 -> 모든 싱글턴 객체들을 관리한다. 해제할 때는 이 Singleton클래스를 통해 해제.

 

싱글톤 패턴을 사용하기 쉽게 매크로를 이용해본다.

#define DECLARE_SINGLETON(className)\
public:\
	static className* GetInstance();\
	virtual void ReleaseInstance();\
private:\
	static className* m_pInstance;

#define CREATE_FUNCTION(className, funcName)\
	static className* ##funcName()\
	{\
		return className::GetInstance();\
	}

#define IMPLEMENT_SINGLETON(className)\
className*	className::m_pInstance = nullptr;\
className*	className::GetInstance()\
{\
	if(m_pInstance == nullptr)\
		m_pInstance = new className();\
	return m_pInstance;\
}\
void className::ReleaseInstance()\
{\
	if(m_pInstance != nullptr)\
	{\
		delete m_pInstance;\
		m_pInstance = nullptr;\
	}\
}

 \는 매크로에서 다음 라인을 의미한다.

 

먼저 DECLARE_SINGLETON 매크로.

이 매크로는 Singleton클래스를 상속받는 단일체 클래스의 내부에 선언되어야 한다.

인자로 상속받는 클래스의 이름을 받으며 이 매크로가 선언되는 클래스는 실제로 단일체 클래스를 만들기 위해 필요한

두개의 함수와 한개의 변수를 선언하게 된다.

 -> 두개의 함수 : GetInstance(), ReleaseInstance() (ReleaseInstance는 순수 가상함수를 통해 구현해야 되는 가상함수)

 -> 한개의 변수 : m_pInstance (단일체 클래스의 단 하나만 존재하는 객체)

 

그 다음 CREATE_FUNCTION

외부에서 쉽게 단일체 클래스의 단일 객체에 접근하기 위한 함수. 

인자로 단일 객체를 호출하기 위한 호출 함수이름을 넘겨준다. (아무거나 상관없지만 클래스명에 맞추어GetUtilManager등등)

 

마지막으로 IMPLEMENT_SINGLETON

단일체 클래스의 cpp파일에 선언되는 매크로.

위에서 DECLARE_SINGLETON의 구현부분이다. 인자로는 상속받는 클래스의 이름을 받는다.

 

이제 싱글톤 클래스를 보자.

#include <list>

class Singleton
{
public:
	// 이 클래스를 상속하는 모든 싱글톤 클래스들을 관리한다.
	using SINGLETON_LIST = std::list<Singleton*>;
	Singleton();
	virtual ~Singleton();
public:
	virtual void ReleaseInstance() abstract; // = 0으로 구현하는 순수 가상함수와 동일. (해당 클래스는 추상 클래스가 된다.)
	static void ReleaseAll();

private:
	static SINGLETON_LIST m_listSingleton;
};

 

#include "Singleton.h"


Singleton::SINGLETON_LIST Singleton::m_listSingleton;

Singleton::Singleton()
{
	m_listSingleton.push_back(this);
}

Singleton::~Singleton()
{
	SINGLETON_LIST::iterator singletonIter = m_listSingleton.begin();
	for (; singletonIter != m_listSingleton.end(); )
	{
		if ((*singletonIter) == this) // 자기자신 파괴
			break;

		++singletonIter;
	}
	m_listSingleton.erase(singletonIter);
}

void Singleton::ReleaseAll()
{
	// 처음 원소는 싱글턴 클래스 자기자신이므로. 거꾸로 지운다.
	SINGLETON_LIST::reverse_iterator singleIter = m_listSingleton.rbegin();
	while (singleIter != m_listSingleton.rend())
	{
		(*singleIter)->ReleaseInstance();
		++singleIter;
	}
	m_listSingleton.clear();
}

소멸자에선 list에 저장된 Singleton 객체를 제거하는 역할을 하며

ReleaseAll에서는 list가 관리하는 싱글톤 객체들을 모두 해제하는 역할을 한다.

 

이제 Singleton클래스를 사용하는 예제를 본다.

#pragma once
#include "Singleton.h"
class UtilManager : public Singleton
{
	DECLARE_SINGLETON(UtilManager);
public:
	int CalcSum(int a, int b) { return a + b; }
};

// 클래스이름은 UtilManager. 함수명은 GetUtilManager로 하였음.
CREATE_FUNCTION(UtilManager, GetUtilManager)
#include "UtilManager.h"


IMPLEMENT_SINGLETON(UtilManager);

위와 같이 싱글턴 클래스들은 매크로를 통해 싱글톤 패턴에서 자주 사용되는 함수와 변수들을 매번 칠 필요없이

간단하게 구현 가능하다.

이제 위의 UtilManager의 단일객체에 접근하여 CaclSum을 사용해보면

int main()
{
    std::cout << GetUtilManager()->CalcSum(10, 20);
}

매우 손쉽게 사용가능하다.

반응형
Comments