Computing

[c++] 싱글톤 디자인 패턴 최적 구현 (Singleton design pattern) 본문

Programming/C++

[c++] 싱글톤 디자인 패턴 최적 구현 (Singleton design pattern)

jhson989 2023. 11. 26. 13:40

Singleton 싱글톤 디자인 패턴

Singleton 디자인 패턴을 따르는 클래스는 프로그램에서 유일한 하나의 클래스 객체만이 생성되도록 강제된다.

 

즉 여러번 클래스의 생성자를 호출하더라도 단 하나의 클래스 객체만이 생성되고, 이후의 생성자 호출은 최초 생성된 클래스 객체만을 반환하도록 구현[1]된다. 

 

(위의 정의 그대로) 프로그램 내에 유일하게 하나의 객체만이 생성되어야 하는 경우에, 싱글톤(싱글턴) 디자인 패턴을 적용하여 클래스를 설계하는 것이 유용하다. 예를 들면 하드웨어 장치(GPU, etc.)나 DB connection, thread-pool과 같은 외부 자원을 이용하는 경우에 프로그램 내에 하나의 대표 객체만을 생성하는 것이 안전하다. 이러한 경우에 싱글톤 디자인 패턴을 이용하여 해당 자원을 대표하는 클래스를 생성한다.

 

 

 

Singleton Implementation

다음은 [1][2]를 참고하여 작성한 최적 C++ Singleton 구현이다. 해당 구현은 correctly-destoryed, thread-safe를 만족한다.

// 싱글톤 클래스
class Singleton {

public:
	// static 함수로 선언
	static Singleton& GetInstance() {
		// staitc 변수로 선언함으로서, instance 뱐수는 한번만 초기화되고, 프로그램 수명 내내 지속됨.
		// 특히 C++11부터 thread-safe 변수 초기화가 보장됨.
 		static Singleton instance;
		return instance;
	}

private:
	// Default 생성자 사용 (필요시 생성자를 원하는데로 수정해서 사용해도 됨)
	Singleton() = default;

	// 객체는 유일하게 하나만 생성되어야 하기에 복사(대입), 이동(대입) 생성자 비활성화
	// 복사, 이동 생성자를 delete로 선언함으로서, 
	// 프로그래머 실수에 의한 복사, 이동 생성자 호출을 원천에 방지할 수 있음.
	Singleton(const Singleton&) = delete;
	Singleton& operator=(const Singleton&) = delete;
	Singleton(Singleton&&) = delete;
	Singleton& operator=(Singleton&&) = delete;
};


// 사용 방법
int main(void) {
	auto& singleton = Singleton::GetInstance();
}

 

 

 

위 방식은 [유일한 하나의 객체 생성]을 보장하는가?

생성자가 private 접근 지정자를 통해 정의되기에 개발자가 해당 클래스의 생성자를 호출하는 것을 방지한다. 그 뿐만 아니라, 실수로 발생할 수 있는 복사 (대입) 생성자, 이동 (대입) 생성자를 아예 delete로 선언해두었기에, 이미 생성된 Singleton 객체를 실수로 복사, 이동하는 경우를 방지한다.

 

유일하게 프로그램 내에서 해당 클래스를 이용할 수 있는 방법은 GetInstance() 함수를 통해 static 변수의 reference를 획득하여 이용하는 방법밖에 없다. 이때 함수의 static (정적) 변수는 함수가 처음 호출될 때 최초 한번 초기화되고[3], 이후 해당 함수를 여러번 호출하더라도 최초 한번 초기화된 변수만이 계속해서 이용된다. 따라서 GetInstance()함수를 여러번 호출하더라도 GenInstance 함수가 처음 불렸을때 생성되었던 static 변수 instance 하나만이 반복적으로 사용되지, 여러 객체가 생성되지 않는다.

 

추가로, function의 static instance는 C++11 표준에서 초기화가 thread-safe하다고 한다[4]. 동시에 여러 개의 thread가 누가 최초인지를 구분 못할 정도의 시간 차를 두고 GetInstance() 함수를 호출할 때, instance가 여러 번 초기화될 수도 있지 않나 생각할 수도 있다. 하지만 [4]에 따르면 변수가 초기화될 때 해당 변수에 접근하는 다른 thread들은 자동으로 초기화가 완료될때까지 기다리도록 프로그램이 설계되었기에 thread-safe하다고 한다.

 

 

 

Reference

[1] https://ko.wikipedia.org/wiki/%EC%8B%B1%EA%B8%80%ED%84%B4_%ED%8C%A8%ED%84%B4

[2] https://stackoverflow.com/questions/1008019/how-do-you-implement-the-singleton-design-pattern

[3] https://stackoverflow.com/a/55570

[4] https://stackoverflow.com/a/1661564