문제의 역사:
싱글턴 패턴은 특정 클래스의 인스턴스를 하나만 생성하도록 제한하기 위해 제안되었으며, 이는 전역 관리자를 구현하는 데 필요했습니다(예: 로거, 리소스 풀, 설정 관리자).
문제:
싱글턴 구현은 간단해 보이지만, C++에서는 스레드 안전성, 파괴의 정확성, 초기화 순서 문제로 어려움이 발생합니다.
해결책:
싱글턴을 구현하는 가장 안전하고 현대적인 방법은 정적 함수 내에 정적 지역 변수를 사용하는 것으로, 이는 첫 번째 호출 시 초기화됨을 보장하며, 스레드 안전성을 제공합니다(C++11 부터) 및 정확한 파괴를 지원합니다.
코드 예시:
class Singleton { public: static Singleton& instance() { static Singleton s; return s; } void doSomething() {} private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
주요 특징:
직렬화나 복제를 통해 두 번째 인스턴스의 싱글턴을 만들 수 있나요?
예. 직렬화/역직렬화 메서드를 구현하거나 복사 생성자를 제한하지 않은 상태에서 clone()을 수동으로 구현하면 두 번째 인스턴스가 생성될 수 있습니다. 이를 피하기 위해 모든 복사, 복제 및 직렬화를 통한 복원을 금지해야 합니다.
C++98/03 표준에서 로컬 정적 변수를 통해 멀티스레드 환경에서 올바른 싱글턴이 구현됩니까?
아니요. C++11 이전의 로컬 정적 변수는 초기화 시 스레드 안전성을 보장하지 않았습니다. 이는 두 개의 스레드가 동시에 인스턴스() 함수에 들어가면 여러 인스턴스가 생성될 수 있습니다. C++11 이후로는 문제가 표준 차원에서 해결되었습니다.
정적 로컬 변수를 통해 생성된 싱글턴 인스턴스는 언제 파괴됩니까?
객체는 프로그램 종료 단계에서 생성의 역순으로(LIFO) 파괴됩니다. 그러나 이는 파괴된 객체에 접근할 경우 문제를 일으킬 수 있습니다.
로그 시스템에서 개발자는 전역 포인터와 new를 사용하여 싱글턴을 구현하고 복사를 금지하는 것을 잊습니다. 프로그램은 작동하지만 멀티스레드 환경에서 때때로 서로 다른 로거 인스턴스가 생성되어 메시지가 손실됩니다.
장점:
단점:
정적 지역 변수를 통해 정적 함수에서 싱글턴이 구현되어 복사가 금지됩니다. 스레드 안전성이 보장되어 프로그램이 확장 가능하고 로그가 올바르게 분리됩니다.
장점:
단점: