design-patterns 싱글톤 - C++싱글 톤 디자인 패턴




자바 singleton (16)

다음과 같이 새 게재 위치를 사용하는 방법 :

class singleton
{
    static singleton *s;
    static unsigned char *buffer[sizeof(singleton)/4 *4] //4 byte align
    static singleton* getinstance()
    {
        if (s == null)
        {
            s = new(buffer) singleton;
        }
        return s;
    }
};

최근에 나는 C ++을위한 Singleton 디자인 패턴의 구현 / 구현에 부딪 혔습니다. 그것은 다음과 같이 보입니다 (저는 이것을 실제 사례에서 채택했습니다) :

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

이 선언에서 인스턴스 필드가 힙에서 시작된다고 추론 할 수 있습니다. 이는 메모리 할당이 있음을 의미합니다. 나에게 완전히 불분명 한 점은 정확히 메모리가 할당 해제 될 때인 것인가? 아니면 버그와 메모리 누수가 있습니까? 구현에 문제가있는 것처럼 보입니다.

내 주요 질문은 올바른 방법으로 구현하는 것입니다.


실제로 힙에서 할당 된 것이지만 소스가 없으면 알 수있는 방법이 없습니다.

전형적인 구현 (이미 emacs에서 가지고있는 코드에서 취함)은 다음과 같습니다.

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

... 그리고 나중에 정리하기 위해 범위를 벗어나는 프로그램에 의존합니다.

정리 작업을 수동으로 수행해야하는 플랫폼에서 작업하는 경우 수동 정리 루틴을 추가 할 것입니다.

이 방법으로 또 다른 문제는 스레드로부터 안전하지 않다는 것입니다. 다중 쓰레드 환경에서, 두 개의 쓰레드는 새로운 인스턴스를 할당 할 기회가 있기 전에 "if"를 통과 할 수있다. 당신이 어쨌든 프로그램 정리에 의존하고 있다면 정리는 너무 크지 않습니다.


할당하지 않은 또 다른 대안 : 필요에 따라 클래스 C 와 같은 싱글 톤을 만듭니다.

singleton<C>()

~을 사용하여

template <class X>
X& singleton()
{
    static X x;
    return x;
}

이것도 Cătălin의 대답은 자동으로 현재 C ++에서는 thread-safe가 아니지만 C ++ 0x에있게됩니다.


메모리 할당을 피할 수 있습니다. 멀티 스레딩 환경에서 문제가되는 많은 변형이 있습니다.

나는 이런 종류의 구현을 선호한다. (사실, 싱글 톤을 가능한 한 많이 피하기 때문에 실제로는 선호한다고 정확하게 말하지는 않는다.)

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

동적 메모리 할당이 없습니다.


누구든지 std::call_oncestd::once_flag 언급 std::once_flag 습니까? 이중 점검 잠금을 비롯한 대부분의 다른 접근 방식이 손상되었습니다.

싱글 톤 패턴 구현의 주요 문제점 중 하나는 안전한 초기화입니다. 유일하게 안전한 방법은 장벽을 동기화하여 초기화 시퀀스를 보호하는 것입니다. 그러나 이러한 장벽 자체는 안전하게 시작되어야합니다. std::once_flag 는 안전 초기화를 보장하는 메커니즘입니다.


#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

예:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);

@ 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() {}
};

정적 개체를 삭제해야하는 정적 함수를 작성해야한다고 생각합니다. 응용 프로그램을 닫을 때이 함수를 호출해야합니다. 이렇게하면 메모리 누출이 발생하지 않습니다.


이것은 객체 수명 관리에 관한 것입니다. 소프트웨어에 싱글 톤 이상을 가지고 있다고 가정합니다. 그리고 그들은 Logger 싱글 톤에 의존합니다. 응용 프로그램이 파괴되는 동안 또 다른 싱글 톤 객체가 Logger를 사용하여 파괴 단계를 기록한다고 가정합니다. 로거를 마지막으로 정리해야한다는 것을 보장해야합니다. 따라서이 백서를 확인하십시오. http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


위에 링크 된 문서에서는 이중 검사 잠금의 단점에 대해 설명합니다. 컴파일러는 객체의 생성자가 호출되기 전에 객체에 대한 메모리를 할당하고 할당 된 메모리의 주소에 대한 포인터를 설정할 수 있습니다. 그러나 C ++에서는 할당자를 사용하여 메모리를 수동으로 할당 한 다음 구조 호출을 사용하여 메모리를 초기화하는 것이 매우 쉽습니다. 이 appraoch를 사용하면 double-checked locking은 잘 동작합니다.


싱글 톤이기 때문에, 당신은 보통 그것이 파괴되기를 원하지 않는다.

프로그램이 종료 될 때 해체되고 할당이 해제됩니다. 이는 싱글 톤에 대한 정상적인 원하는 동작입니다. 클래스를 명시 적으로 정리할 수있게하려면 클래스에 클래스를 추가하여 클래스를 깨끗한 상태로 복원하고 다음에 사용 된 시간에 다시 할당 할 수있는 정적 메서드를 추가하는 것이 매우 쉽습니다.하지만 이는 클래스의 범위를 벗어납니다. "고전적인"싱글 톤.


단순 싱글 톤 클래스, 이것은 헤더 클래스 파일이어야합니다.

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

다음과 같이 싱글 톤에 액세스하십시오.

sSingletonClass->Relocate(1, 2, 5);

수용된 대답의 해법은 중대한 단점을 가지고 있습니다 - 싱글 톤에 대한 소멸자는 컨트롤이 main() 함수를 떠난 후에 호출됩니다. 일부 종속 객체가 main 내부에 할당되면 실제로 문제가 발생할 수 있습니다.

Qt 애플리케이션에서 Singleton을 도입하려고 할 때이 문제를 만났습니다. 모든 셋업 대화 상자는 싱글 톤이어야하고 위 패턴을 채택하기로 결정했습니다. 불행히도, Qt의 메인 클래스 인 QApplicationmain 함수의 스택에 할당되었고, Qt는 어플리케이션 객체가 없을 때 대화 상자를 생성 / 파괴하는 것을 금지했습니다.

그래서 힙 할당 싱글 톤을 선호합니다. 모든 싱글 톤에 대해 명시적인 init()term() 메서드를 제공하고 main 내부에서 호출합니다. 따라서 싱글 톤 생성 / 파괴의 순서에 대한 완전한 제어권을 가지며, 누군가가 getInstance() 호출했는지 여부에 관계없이 싱글 톤이 생성된다는 것을 보장합니다.


다음은 쉬운 구현입니다.

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

하나의 오브젝트 만 작성되고 이후에 매번 오브젝트 참조가 리턴됩니다.

SingletonClass instance created!
00915CB8
00915CB8

여기서 00915CB8은 싱글 톤 Object의 메모리 위치이며, 프로그램이 실행되는 동안 동일하지만 (일반적으로!) 프로그램이 실행될 때마다 다릅니다.

NB 이것은 스레드로부터 안전하지 않습니다. 스레드 안전을 보장해야합니다.


개체를 힙에 할당하려면 고유 한 포인터를 사용하지 않는 것이 좋습니다. 우리는 유일한 포인터를 사용하고 있기 때문에 메모리도 할당 해제됩니다.

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

필자가 본 최고의 싱글 톤 패턴은 Supplier 인터페이스를 사용합니다.

  • 그것은 일반적이고 재사용 가능하다.
  • lazy 초기화를 지원합니다.
  • 초기화 될 때까지만 동기화되고 차단 공급자는 차단 공급자가 아닙니다.

아래 참조 :

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}




c++ design-patterns singleton