c++ 싱글톤 자바 - C ++ 싱글 톤 디자인 패턴





9 Answers

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

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

singleton pattern linux

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

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

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

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




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

singleton<C>()

~을 사용하여

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

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




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

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);



다음은 쉬운 구현입니다.

#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 이것은 스레드로부터 안전하지 않습니다. 스레드 안전을 보장해야합니다.




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




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

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




여기 다른 토론 외에도 하나의 인스턴스에만 사용을 제한하지 않고 전역 성을 가질 수 있다는 점은 가치가 있습니다. 예를 들어 참조 계산의 경우를 생각해보십시오.

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

이제 어딘가에 main 과 같은 함수 내부에서 할 수 있습니다.

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

ref는 컴파일시에 정보가 제공되기 때문에 포인터를 각각의 Store 다시 저장할 필요가 없습니다. 컴파일러가 전역 적이어야하므로 Store 의 수명에 대해 걱정할 필요가 없습니다. Store 인스턴스가 실제로 단 하나라면이 방법에는 오버 헤드가 없습니다. 하나 이상의 인스턴스에서 컴파일러는 코드 생성에 대해 영리해야합니다. 필요한 경우 ItemRef 클래스를 Storefriend 로 만들 수도 있습니다 (템플릿이있는 친구도 가질 수 있습니다!).

Store 자체가 템플릿 기반 클래스 인 경우 작업이 복잡해 지지만 다음과 같은 서명으로 도우미 클래스를 구현하여이 메서드를 사용할 수도 있습니다.

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

사용자는 이제 각 전역 Store 인스턴스에 대해 StoreWrapper 유형 (및 전역 인스턴스)을 만들 수 있으며 항상 래퍼 인스턴스를 통해 저장소에 액세스합니다. 따라서 Store 를 사용하기 위해 필요한 템플릿 매개 변수의 세부 정보를 잊어 버릴 수 있습니다.




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




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

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;
    }
};



Related