your programing

C ++ 싱글 톤 디자인 패턴

lovepro 2020. 9. 30. 11:13
반응형

C ++ 싱글 톤 디자인 패턴


최근에 저는 C ++ 용 싱글 톤 디자인 패턴의 실현 / 구현에 부딪 혔습니다. 다음과 같이 보였습니다 (실제 사례에서 채택했습니다).

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

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

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


2008 년에 저는 게으른 평가되고, 파괴가 보장되고, 기술적으로 스레드로부터 안전하지 않은 Singleton 디자인 패턴의 C ++ 98 구현을 제공했습니다. C ++에서 Singleton
샘플을 제공 할 수 있습니까?

다음은 지연 평가되고 올바르게 파괴되고 스레드로부터 안전한 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 메서드의 정적 변수로 선언 된 Singleton 인스턴스, 스레드로부터 안전합니까?

이중 확인 잠금이 C ++에서 작동하지 않는 이유를 설명하는이 기사를 참조하십시오
. C ++ 프로그래머가 알아야하는 정의되지 않은 일반적인 동작은 무엇입니까?
Dobbs 박사 : C ++ 및 이중 확인 잠금의 위험 : 1 부


Singleton이기 때문에 일반적으로 파괴되는 것을 원하지 않습니다.

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


메모리 할당을 피할 수 있습니다. 다중 스레딩 환경의 경우 문제가있는 많은 변형이 있습니다.

나는 이런 종류의 구현을 선호합니다 (실제로 나는 가능한 한 싱글 톤을 피하기 때문에 내가 선호한다고 정확하게 말하지 않았습니다) :

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에 있습니다.


허용되는 답변의 솔루션에는 중요한 단점이 main()있습니다. 컨트롤이 함수를 떠난 후에 싱글 톤에 대한 소멸자가 호출 됩니다. 일부 종속 객체가 내부에 할당되면 실제로 문제가 발생할 수 있습니다 main.

Qt 응용 프로그램에 Singleton을 도입하려고 할 때이 문제를 만났습니다. 모든 설정 대화 상자가 싱글 톤이어야한다고 결정하고 위의 패턴을 채택했습니다. 불행히도 Qt의 메인 클래스 QApplicationmain함수의 스택에 할당되었으며 Qt는 응용 프로그램 개체를 사용할 수 없을 때 대화 상자를 생성 / 파괴하는 것을 금지합니다.

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


힙에 개체를 할당하려면 고유 포인터를 사용하지 않는 이유는 무엇입니까? 고유 포인터를 사용하고 있기 때문에 메모리도 할당 해제됩니다.

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

답변 중 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>


다음은 쉬운 구현입니다.

#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

Here 00915CB8 is the memory location of singleton Object, same for the duration of the program but (normally!) different each time the program is run.

N.B. This is not a thread safe one.You have to ensure thread safety.


It is indeed probably allocated from the heap, but without the sources there is no way of knowing.

The typical implementation (taken from some code I have in emacs already) would be:

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

...and rely on the program going out of scope to clean up afterwards.

If you work on a platform where cleanup must be done manually, I'd probably add a manual cleanup routine.

Another issue with doing it this way is that it isn't thread-safe. In a multithreaded environment, two threads could get through the "if" before either has a chance to allocate the new instance (so both would). This still isn't too big of a deal if you are relying on program termination to clean up anyway.


Has anyone mentioned std::call_once and std::once_flag? Most other approaches - including double checked locking - are broken.

One major problem in singleton pattern implementation is safe initialization. The only safe way is to guard the initialization sequence with synchronizing barriers. But those barriers themselves need to be safely initiated. std::once_flag is the mechanism to get guaranteed safe initialization.


In addition to the other discussion here, it may be worth noting that you can have global-ness, without limiting usage to one instance. For example, consider the case of reference counting something...

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

Now somewhere inside a function (such as main) you can do:

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

The refs don't need to store a pointer back to their respective Store because that information is supplied at compile-time. You also don't have to worry about the Store's lifetime because the compiler requires that it is global. If there is indeed only one instance of Store then there's no overhead in this approach; with more than one instance it's up to the compiler to be clever about code generation. If necessary, the ItemRef class can even be made a friend of Store (you can have templated friends!).

If Store itself is a templated class then things get messier, but it is still possible to use this method, perhaps by implementing a helper class with the following signature:

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>. */ };

The user can now create a StoreWrapper type (and global instance) for each global Store instance, and always access the stores via their wrapper instance (thus forgetting about the gory details of the template parameters needed for using Store).


This is about object life-time management. Suppose you have more than singletons in your software. And they depend on Logger singleton. During application destruction, suppose another singleton object uses Logger to log its destruction steps. You have to guarantee that Logger should be cleaned up last. Therefore, please also check out this paper: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


The paper that was linked to above describes the shortcoming of double checked locking is that the compiler may allocate the memory for the object and set a pointer to the address of the allocated memory, before the object's constructor has been called. It is quite easy in c++ however to use allocaters to allocate the memory manually, and then use a construct call to initialize the memory. Using this appraoch, the double-checked locking works just fine.


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

Example:

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

    public:
        INS(CCtrl);

Simple singleton class, This must be your header class file

#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

Access your singleton like this:

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

I think You should write a static function wherein your static object is deleted. You should call this function when you are about to close your application. This will ensure you dont have memory leakage.


How about using placement new like this:

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

참고URL : https://stackoverflow.com/questions/1008019/c-singleton-design-pattern

반응형