"Design Pattern"에서 Singleton은 반환 포인터로 작성됩니다.
class Singleton{ public: static Singleton* Instance(); protected: Singleton(); private: static Singleton* _instance; };
해당 구현 cpp 파일은 다음과 같습니다.
Singleton* Singleton::_instance; Singleton* Singleton::Instance(){ if( _instance == 0){ _instance = new Singleton; }; return _instance; }
생성자를 설계하는 목적은 다음과 같습니다. protected는 클래스 외부에서 New를 방지하기 위한 것입니다. 누군가가 이를 비공개로 설계할 수 있습니다. 이 클래스의 상속 가능성을 고려한다면 생성자를 protected로 설계하는 것이 좋으며 가상 소멸자도 추가해야 합니다. 다른 사람이 Singleton 객체를 복사하는 것을 방지하려면:
Singleton* pSingleton = Singleton::Instance(); Singleton s1 = *pSingleton; Singleton s2 = *pSingleton; 需要将拷贝构造(copy constructor)函数变成 private。
그러나 여기서 문제는 Singleton 객체를 언제 삭제해야 합니까? C++의 기본 원칙에 따르면 객체는 생성된 곳마다 소멸됩니다. 여기에는 Singleton 객체를 삭제하는 destroy 메소드도 있어야 합니다. 삭제하는 것을 잊어버리면 더 번거롭습니다. Instance 함수에도 여러 스레드가 동시에 접근하는 잠금 문제가 있습니다. Instance 기능의 시작과 끝 부분에 Lock과 Unlock을 배치하게 되면 전체 기능의 성능이 많이 떨어지게 됩니다. 이것은 좋은 디자인이 아닙니다.
싱글톤 객체 삭제를 잊어버려서 발생하는 메모리 누수 문제를 방지할 수 있는 작은 변경 사항이 있습니다. 즉, std:auto_ptr을 사용하여 Singleton 객체를 포함하고, 클래스 정적 멤버 auto_ptr 객체를 정의하고, 정적 auto_ptr 변수가 소멸될 때 Singleton 객체를 자동으로 삭제하는 것입니다. 사용자가 싱글톤 개체를 삭제하는 것을 방지하려면 소멸자를 공개에서 보호로 변경해야 합니다. 다음은 헤더 파일 SingletonAutoPtr.h입니다:
#include <memory> using namespace std; class CSingletonAutoPtr { private: static auto_ptr<CSingletonAutoPtr> m_auto_ptr; static CSingletonAutoPtr* m_instance; protected: CSingletonAutoPtr(); CSingletonAutoPtr(const CSingletonAutoPtr&); virtual ~CSingletonAutoPtr(); //allow auto_ptr to delete, using protected ~CSingletonAutoPtr() friend class auto_ptr<CSingletonAutoPtr>; public: static CSingletonAutoPtr* GetInstance(); void Test(); };
#p#해당 SingletonAutoPtr.cpp는 다음과 같습니다.
#include "SingletonAutoPtr.h" #include <iostream> //initial static member vars here CSingletonAutoPtr* CSingletonAutoPtr::m_instance = NULL; auto_ptr<CSingletonAutoPtr> CSingletonAutoPtr::m_auto_ptr; ///////////////////////////////////////// // Construction/Destruction ///////////////////////////////////////// CSingletonAutoPtr::CSingletonAutoPtr() { cout << "CSingletonAutoPtr::CSingletonAutoPtr()" << endl; //put single object into auto_ptr object m_auto_ptr = auto_ptr<CSingletonAutoPtr>(this); } CSingletonAutoPtr::~CSingletonAutoPtr() { cout << "CSingletonAutoPtr::~CSingletonAutoPtr()" << endl; } CSingletonAutoPtr* CSingletonAutoPtr::GetInstance() { //begin lock //.... if(m_instance == NULL) m_instance = new CSingletonAutoPtr(); //end lock //... return m_instance; } void CSingletonAutoPtr::Test() { cout << "CSingletonAutoPtr::Test()" << endl; }
호출 방법:
CSingletonAutoPtr* pSingleton = CSingletonAutoPtr::GetInstance(); pSingleton->Test();
Write one C++의 싱글톤에는 그렇게 많은 노력이 필요하다는 것은 우리의 기대 이상입니다. auto_ptr을 사용해 본 적이 없는 사람도 많으며, std:auto_ptr 자체도 객체 소유권 메커니즘을 기반으로 하고 있지만, Apache Log4cxx에는 객체 계산을 기반으로 하여 사용하기 쉬운 auto_ptr이 있습니다. . 좋은 auto_ptr을 사용하기 위해 log4cxx를 사용해야 하는 것은 많은 프로젝트에 좋지 않습니다. 물론 위 예제를 작성하는 데에는 ANSI C++ STL의 std:auto_ptr이면 충분합니다.
#p# 또 다른 아이디어는 GetInstance 함수를 정적 멤버로 설계하는 것이 더 나을 수 있다는 것입니다. 왜냐하면 일반적으로 싱글톤 개체는 크지 않기 때문입니다. 정적 멤버는 항상 메모리를 점유해야 하지만 크지는 않습니다. 문제. 여기서 소멸자는 공개로 설정되어야 합니다. 다음은 헤더 파일 SingleStaticObj.h
class CSingletonStaticObj { private: static CSingletonStaticObj m_instance; protected: CSingletonStaticObj(); CSingletonStaticObj(const CSingletonStaticObj&); public: virtual ~CSingletonStaticObj(); //must public static CSingletonStaticObj& GetInstance(); void Test(); }; 对应的 SingleStaticObj.cpp 文件为: #include "SingletonStaticObj.h" #include <string> #include <iostream> using namespace std; CSingletonStaticObj CSingletonStaticObj::m_instance; CSingletonStaticObj::CSingletonStaticObj() { cout << "CSingletonStaticObj::CSingletonStaticObj()" << endl; } CSingletonStaticObj::~CSingletonStaticObj() { cout << "CSingletonStaticObj::~CSingletonStaticObj()" << endl; } CSingletonStaticObj& CSingletonStaticObj::GetInstance() { return m_instance; } void CSingletonStaticObj::Test() { cout << "CSingletonStaticObj::Test()" << endl; }
호출 방법입니다.
CSingletonStaticObj& singleton = CSingletonAutoPtr::GetInstance();singleton.Test();
코드 크기 측면에서는 정적 멤버 ref를 사용하는 것이 더 간단한 것 같습니다. 나는 이 방법을 선호한다.
그러나 정적 멤버 싱글톤이 모든 상황에 적합한 것은 아닙니다. 예를 들어, 다른 인스턴스를 반환하기로 동적으로 결정해야 하는 경우에는 GetInstance를 사용할 수 없습니다. 예를 들어 FileSystem::GetInstance는 Windows에서 실행될 때 새 WinFileSystem을 반환해야 할 수 있고 Linux/Unix에서 실행될 때 새 LinuxFileSystem을 반환해야 할 수 있습니다. 이 경우에도 싱글톤 포인터가 포함된 위의 auto_ptr 메서드를 사용해야 합니다.