C++ 单例模板
懒汉式
懒汉式:比较懒,所以用的时候才初始化,所以是线程不安全的。所以需要做出一点小技巧让它变得线程安全
由于单例模板类本身不需要被创建拷贝或赋值,因此可以删除
template<typename MotionType>
class Singleton
{
public:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
接着声明pInstance
,类型为MotionType*
,GetInstance
方法,返回值为MotionType*
private:
static MotionType* pInstance;
public:
static MotionType* GetInstance();
静态成员需要在类外进行初始化
template<class MotionType>
MotionType* Singleton<MotionType>::pInstance = nullptr;
实现获取单例的静态方法,注意new
的类型应为MotionType
template<class MotionType>
inline MotionType* Singleton<MotionType>::GetInstance()
{
if (pInstance == nullptr)
pInstance = new MotionType();
return pInstance;
}
整体代码如下,书写在Singleton.hpp中
template<class MotionType>
class Singleton
{
public:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static MotionType* GetInstance();
private:
static MotionType* pInstance;
};
template<class MotionType>
MotionType* Singleton<MotionType>::pInstance = nullptr;
template<class MotionType>
inline MotionType* Singleton<MotionType>::GetInstance()
{
if (pInstance == nullptr)
pInstance = new MotionType();
return pInstance;
}
在构建好懒汉式单例模板类后,可以设计一个具体的类来测试模板类是否工作正常
实现GotoCave单例类,GotoCave类同样需要重载默认构造,拷贝构造以及 = 运算符并设置为private。需要注意的是GotoCave类中应将Singleton类设为友元,以让Singleton类能够调用到GotoCave类的构造函数
class GotoCave
{
private:
friend class Singleton<GotoCave>;
GotoCave() {
// Init your class
}
~GotoCave() {
// Freed your class
}
public:
GotoCave(const GotoCave&) = delete;
GotoCave& operator=(const GotoCave&) = delete;
// Whatever you want...
};
实际使用中,当模板参数调用到别的头文件中的类时,需要在当前头文件中提前声明,否则编译器将报错 “ xxx:找不到提示 ”
class GotoCave;
// Too many codes..
auto temp = Singleton<GotoCave>::GetInstance();
但是,以上代码存在问题。pInstance
作为模板类的成员变量,在程序结束的时候并不会调用GotoCave
的析构函数,这样就会导致内存泄漏;并且在多线程的情况下,某个线程执行new
的同时,另一个线程可能正在对pInstance
判空,而此时实例还未被创建出来,这并不是我们期望的结果。在Effective C++中提到了一种Meyers' Singleton的方法。该方法不仅能防止内存泄漏,还具有线程安全性。改进代码如下
[^Magic Static]: If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. 如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。
template<class MotionType>
class Singleton
{
public:
Singleton();
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static MotionType& GetInstance();
};
template<class MotionType>
inline MotionType& Singleton<MotionType>::GetInstance()
{
static MotionType Instance;
return Instance;
}
饿汉式
饿汉式,比较饿,程序一开始就迫不及待的进行初始化,因为程序开始运行时就创建完毕,多线程只有读操作,所以是线程安全的
实现方法与懒汉式类似
template<class MotionType>
class Singleton
{
public:
Singleton() = delete;
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static MotionType& GetInstance();
private:
static MotionType Instance;
};
template<class MotionType>
MotionType Singleton<MotionType>::Instance;
template<class MotionType>
inline MotionType& Singleton<MotionType>::GetInstance()
{
return Instance;
}
由于使用了模板类,因此只有代码中使用到对应的单例模板时,编译期才会生成相应的单例类代码。由于是饿汉式单例,具体类的初始化还是还是会执行在main函数之前
更深层次的懒汉式单例
- 使用智能指针管理内存,解决了析构的问题
- 使用可变模板参数,解决了不同类的构造问题
- 使用锁,解决了多线程下读写的安全性问题
// SmartSingleton.hpp
#include<memory>
#include<mutex>
template<typename SType>
class SmartSingleton
{
public:
template<typename... Args>
static std::shared_ptr<SType> GetInstance(Args&&... args)
{
std::lock_guard<std::mutex> lg(mutexLock);
if (pInstance == nullptr)
{
// 缺点 调用不到私有的构造函数 除非对应类将std::make_shared设置为友元函数
// pInstance = std::make_shared<SType>(std::forward<Args>(args)...);
pInstance = std::shared_ptr<SType>(new SType(std::forward<Args>(args)...));
}
return pInstance;
}
SmartSingleton() = delete;
SmartSingleton(const SmartSingleton&) = delete;
SmartSingleton& operator=(const SmartSingleton&) = delete;
private:
static std::shared_ptr<SType> pInstance;
static std::mutex mutexLock;
};
template<typename SType>
std::shared_ptr<SType> SmartSingleton<SType>::pInstance = nullptr;
template<typename SType>
std::mutex SmartSingleton<SType>::mutexLock;
测试类
class MyClass
{
friend class SmartSingleton<MyClass>;
private:
MyClass(std::string _name, int _num) : name(std::move(_name)), num(_num) { std::cout << "MyClass" << std::endl; }
std::string name;
int num;
public:
~MyClass() { std::cout << "~MyClass" << std::endl; }
};
测试代码
int main()
{
std::shared_ptr<MyClass> p = SmartSingleton<MyClass>::GetInstance("Chen", 10);
// Whatever you want to do with p...
}