최근에 저는 C ++에 대한 싱글 톤 디자인 패턴의 구현 / 구현에 부딪 쳤습니다. 다음과 같이 보입니다 (실제 예제에서 채택했습니다).
// a lot of methods are omitted here
class Singleton
{
public:
static Singleton* getInstance( );
~Singleton( );
private:
Singleton( );
static Singleton* instance;
};
이 선언에서 인스턴스 필드가 힙에서 시작된 것으로 추론 할 수 있습니다. 이는 메모리 할당이 있음을 의미합니다. 메모리가 정확히 할당 해제 될 때가 확실하지 않은 것은 무엇입니까? 아니면 버그와 메모리 누수가 있습니까? 구현에 문제가있는 것 같습니다.
내 주요 질문은 올바른 방법으로 구현하는 것입니다.
답변
2008 년에 나는 기술적으로 스레드 안전하지 않은 게으른 평가되고, 파괴가 보장되고 파괴되지 않는 Singleton 디자인 패턴의 C ++ 98 구현을
제공했다.
다음은 게으른 평가, 올바르게 파괴되고 스레드로부터 안전한 Singleton 디자인 패턴의 업데이트 된 C ++ 11 구현입니다 .
class S
{
public:
static S& getInstance()
{
static S instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
S() {} // Constructor? (the {} brackets) are needed here.
// C++ 03
// ========
// Don't forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
S(S const&); // Don't Implement
void operator=(S const&); // Don't implement
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
public:
S(S const&) = delete;
void operator=(S const&) = delete;
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
};
싱글 톤 사용시기에 대한이 기사를 참조하십시오
.
초기화 순서 및 처리 방법에 대한이 두 기사를 참조하십시오.
정적 변수 초기화 순서
C ++ 정적 초기화 순서 문제 찾기
수명을 설명하는이 기사를 참조하십시오
. C ++ 함수에서 정적 변수의 수명은 얼마입니까?
싱글 톤에 대한 스레딩 영향에 대해 설명하는이 기사를 참조하십시오.
GetInstance 메소드의 정적 변수로 선언 된 싱글 톤 인스턴스는 스레드로부터 안전합니까?
C ++에서 이중 검사 잠금이 작동하지 않는 이유를 설명하는이 기사를 참조하십시오
. C ++ 프로그래머가 알아야 할 일반적인 정의되지 않은 동작은 무엇입니까?
Dr Dobbs : C ++ 및 이중 검사 잠금 위험 : 1 부
답변
싱글 톤이기 때문에 일반적으로 파괴하기를 원하지 않습니다.
프로그램이 종료되면 단절되고 할당이 해제되며 이는 싱글 톤에 대한 정상적인 동작입니다. 명시 적으로 정리하고 싶다면 클래스에 정적 메소드를 추가하여 깨끗한 상태로 복원하고 다음에 사용할 때 다시 할당 할 수는 있지만 그 범위를 벗어납니다. “클래식”싱글 톤.
답변
메모리 할당을 피할 수 있습니다. 멀티 스레딩 환경의 경우 문제가있는 많은 변형이 있습니다.
나는 이런 종류의 구현을 선호한다. (실제로, 싱글 톤을 가능한 많이 피하기 때문에 내가 선호한다고 올바르게 말하지는 않는다) :
class Singleton
{
private:
Singleton();
public:
static Singleton& instance()
{
static Singleton INSTANCE;
return INSTANCE;
}
};
동적 메모리 할당이 없습니다.
답변
@Loki Astari의 대답 은 훌륭합니다.
그러나 여러 정적 개체가있는 경우에는 싱글 톤 을 사용하는 모든 정적 개체가 더 이상 필요하지 않을 때까지 싱글 톤 이 손상 되지 않도록 보장 해야합니다.
이 경우 정적 소멸자가 프로그램 끝에서 호출되는 경우에도 모든 사용자에게 싱글 톤std::shared_ptr
을 유지하는 데 사용할 수 있습니다 .
class Singleton
{
public:
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
static std::shared_ptr<Singleton> instance()
{
static std::shared_ptr<Singleton> s{new Singleton};
return s;
}
private:
Singleton() {}
};
답변
또 다른 비 할당 대안 : C
필요한 경우 class 와 같이 싱글 톤을 만듭니다 .
singleton<C>()
사용
template <class X>
X& singleton()
{
static X x;
return x;
}
이것과 Cătălin의 대답은 현재 C ++에서 자동으로 스레드 안전하지 않지만 C ++ 0x에 있습니다.
답변
답변 중 CRTP 구현을 찾지 못 했으므로 여기에 있습니다.
template<typename HeirT>
class Singleton
{
public:
Singleton() = delete;
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
static HeirT &instance()
{
static HeirT instance;
return instance;
}
};
사용하려면 다음과 같이 클래스를 상속하십시오. class Test : public Singleton<Test>
답변
허용 된 답변의 솔루션에는 상당한 단점이 main()
있습니다. 컨트롤이 함수를 떠난 후 싱글 톤의 소멸자가 호출 됩니다. 일부 종속 객체가 내부에 할당되면 실제로 문제가 발생할 수 있습니다 main
.
Qt 응용 프로그램에 Singleton을 도입하려고 할 때이 문제가 발생했습니다. 모든 설정 대화 상자가 싱글 톤이어야하고 위의 패턴을 채택하기로 결정했습니다. 불행히도 Qt의 메인 클래스 QApplication
는 main
함수의 스택에 할당되었으며 Qt는 사용 가능한 응용 프로그램 객체가 없을 때 대화 상자를 만들거나 파괴하는 것을 금지합니다.
그렇기 때문에 힙 할당 싱글 톤을 선호합니다. 나는 모든 싱글 톤에 대한 명시 init()
적이고 term()
메소드를 제공 하고 내부에서 호출합니다 main
. 따라서 싱글 톤 생성 / 파괴 순서를 완전히 제어 할 수 있으며 누군가 호출했는지 여부에 관계없이 싱글 톤이 생성되도록 보장합니다 getInstance()
.