Home >Backend Development >C#.Net Tutorial >Implementation of Singleton class in C++
In "Design Pattern", Singleton is written as a return pointer:
class Singleton{ public: static Singleton* Instance(); protected: Singleton(); private: static Singleton* _instance; };
The corresponding implementation cpp file is:
Singleton* Singleton::_instance; Singleton* Singleton::Instance(){ if( _instance == 0){ _instance = new Singleton; }; return _instance; }
The purpose of designing the constructor as protected is to prevent new outside the class. Some people may design it as private. If you consider If it is possible to inherit this class, it is better to design the constructor as protected, and also need to add a virtual destructor. To prevent others from copying the Singleton object:
Singleton* pSingleton = Singleton::Instance(); Singleton s1 = *pSingleton; Singleton s2 = *pSingleton; 需要将拷贝构造(copy constructor)函数变成 private。
But the question here is, when to delete the Singleton object? According to a basic principle of C++, the object is destroyed wherever it is created. There should also be a destroy method here to delete the Singleton object. It will be more troublesome if you forget to delete it. The Instance function also has the locking problem of simultaneous access by multiple threads. If locking and unlocking are placed at the beginning and end of the Instance function, the performance of the entire function will drop a lot. This is not a good design.
There is a small change that can avoid the memory leak problem caused by forgetting to delete the Singleton object. That is to use std:auto_ptr to contain the Singleton object, define a class static member auto_ptr object, and automatically delete the Singleton object when the static auto_ptr variable is destructed. In order to prevent users from deleting Singleton objects, the destructor needs to be changed from public to protected. The following is the header file 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# corresponds to SingletonAutoPtr.cpp as follows:
#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; }
Calling method:
CSingletonAutoPtr* pSingleton = CSingletonAutoPtr::GetInstance(); pSingleton->Test();
Writing a Singleton in C++ requires so much effort, which is beyond our expectation. There are many people who have never used auto_ptr, and std:auto_ptr itself is not perfect. It is based on the object ownership mechanism. In contrast, there is an auto_ptr in Apache Log4cxx, which is based on object counting and is easier to use. Having to use log4cxx just to use a good auto_ptr is not good for many projects. Of course, std:auto_ptr in ANSI C++'s STL is sufficient for writing the above example.
#p#Another idea is that it may be better to design the GetInstance function as a static member, because generally speaking, Singleton objects are not large. Although static members must always occupy memory, it is not a big problem. The destructor here must be set to public. The following is the header file 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; }
The calling method:
CSingletonStaticObj& singleton = CSingletonAutoPtr::GetInstance();singleton.Test();
In terms of code size, it seems that using static member ref is simpler. I prefer this method.
However, static member singleton is not suitable in all situations. For example, GetInstance cannot be used when it needs to dynamically decide to return different instances. For example, FileSystem::GetInstance may need to return new WinFileSystem when running under Windows, and may need to return new LinuxFileSystem when running under Linux/Unix. In this case, you still need to use the above auto_ptr method containing a singleton pointer.