C ++ 싱글 톤 디자인 패턴 private:

최근에 저는 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의 메인 클래스 QApplicationmain함수의 스택에 할당되었으며 Qt는 사용 가능한 응용 프로그램 객체가 없을 때 대화 상자를 만들거나 파괴하는 것을 금지합니다.

그렇기 때문에 힙 할당 싱글 톤을 선호합니다. 나는 모든 싱글 톤에 대한 명시 init()적이고 term()메소드를 제공 하고 내부에서 호출합니다 main. 따라서 싱글 톤 생성 / 파괴 순서를 완전히 제어 할 수 있으며 누군가 호출했는지 여부에 관계없이 싱글 톤이 생성되도록 보장합니다 getInstance().