본문 바로가기
공부/C, C++

[C++] 싱글톤 패턴(1) - 동적 할당 방식

by 김샤랑 2022. 9. 8.

[C++]  싱글톤 패턴(1) - 동적 할당 방식


 

오랜만에 포스팅을 쓴다.

최근에 디자인 패턴중 하나인 싱글톤 패턴에 대해 배웠다.

역시 기록해두는 일이 머릿속에서도 정리가 잘되고 기억이 오래 남는 것 같다.

 

본 포스팅은 발상 => 문제발생 => 다음 발상 => 문제 발생 => 해결 느낌으로 진행된다.

순서대로 차근차근 따라 해보자.

 


0. 싱글톤 패턴?

: 싱글톤 패턴은 해당 클래스로 만들 수 있는 객체를 단 1개로 제한하는 방식을 말한다.

근데 객체의 개수는 1개지만 어디서든 쉽게 접근해서 호출할 수 있어야 한다.

 


1. 가장 먼저 떠오르는 방식은 생성자를 숨기는 것이다. (private)

GameEngine이라는 클래스가 있다고 가정하자.

 

class GameEngine   
{
// GameEngine.h 파일이다.
private:
    GameEngine();
    ~GameEngine();
};

 

이렇게 하면 생성자가 객체 생성될 때 호출이 되는데

이 함수를 private로 숨겨서 외부에서 호출할 수 없게 만들면, 객체 생성을 할 수 없게 돼버린다.

 

 

이 상황에서 다른 파일에서 GameEngine test; 를 써서 객체 생성을 하려고 하면 GameEngine::GameEngine()에 액세스 할 수 없다는 오류가 뜨게 된다.

 

하지만 이 방식은 선언 자체가 불가능해져서 객체 생성이 안된다. (제로톤?)

 


2. 본인 클래스 내에서는 생성자 접근 가능! => 자기 스스로를 만들어주는 멤버 함수를 만든다. 

객체를 만드는 방법을 특정 함수로 제한한다.

GetInstance() 멤버 함수를 만들었다.

 

class GameEngine
{

public: 
	GameEngine* GetInstance()
    {
    
    	return nullptr;
    }

private:
    GameEngine();
    ~GameEngine();
};

 

하지만 이 멤버함수를 호출하려면 객체가 있어야 한다.

객체를 만들어주는 함수를 쓰려면 이미 객체가 있어야 하는 말도 안 되는 상황이 돼버린다!

 

 


3. 객체가 없어도 호출할 수 있는 정적 멤버 함수를 만든다. (static)

static(정적 변수)은 객체가 있어도 객체가 없어도 호출 가능하다. 

만약 static에 대한 개념을 모른다면 구글링으로 빠르게 배우고 오자!

정적 변수는 "데이터" 메모리 영역에 선언된다.

 

GetInstance() 함수를 정적 멤버 함수로 바꾸었다.

 

class GameEngine
{

public: 
	static GameEngine* GetInstance()
    {
    	
    	return new GameEngine;
    }

private:
	GameEngine();
    ~GameEngine();
};

 

 

// main.cpp

GameEngine* core = GameEngine::GetInstance();

 

생성된 객체의 주소를 받아서 core에 담는다.

하지만 이 방식이면 하나의 객체만 생성되도록 제한된 게 아니라 쓸 때마다 객체가 생성된다는 문제점이 있다.

 

즉 분기점을 나누어야 한다.

 

 


4. 싱글톤 구현

// GameEngine.h

class GameEngine
{
private: 
	static GameEngine* g_PInst;
	
public: 
    static GameEngine* GetInstance()
    {
    	// 최초 호출 된 경우 ( static 변수로 받는다.)
        if(nullptr == g_PInst)
        	g_PInst = new GameEngine; // 동적 할당
        
	// 2번 부터는 그냥 주소 반환
        return g_PInst;
    }
    
private:
    GameEngine();
    ~GameEngine();
};

 

// GameEngine.cpp
// 초기화를 해둔다. 

#include "GameEngine.h"

GameEngine* GameEngine::g_PInst = nullptr;

 

정적 멤버 함수로 객체를 생성하는데 그 주소마저 정적 변수에 담는다. 

그 주소를 리턴해주기만 하면 객체는 하나만 생성!

 


5. 객체 소멸 (Release() )

// GameEngine.h

class GameEngine
{
private: 
	static GameEngine* g_PInst;
	
public: 
    static GameEngine* GetInstance()
    {
    	// 최초 호출 된 경우 ( static 변수로 받는다.)
        if(nullptr == g_PInst)
        	g_PInst = new GameEngine; // 동적 할당
        
	// 2번 부터는 그냥 주소 반환
        return g_PInst;
    }
    
    static void Release()
    {
    	if(nullptr != g_PInst)
    	{
    		delete g_PInst;
        	g_Pinst = nullptr;
   	} 
    }
    
private:
    GameEngine();
    ~GameEngine();
};

 


6. 끝인가??? => NO

사실 싱글톤 방식의 구현 방법은 여러 가지가 있다. 이 방법은 그 방법들 중 하나이다.

다른 방법은 다음 포스팅에서 이어진다.(데이터 영역에 올리기)

 

 

댓글