前面我们介绍了单例模式的两种实现:懒汉模式和饿汉模式,今天我们以新的方式来实现可复用的单例模式。
奇异递归模板是指父类是个模板类,模板类型是子类类型,即父类通过模板参数可以知道子类的类型。
// brief: a singleton base class offering an easy way to create singleton
#include <iostream>
template<typename T>
class Singleton{
public:
static T& Instance(){
static T instance;
return instance;
}
Singleton(const Singleton&)=delete;
Singleton& operator =(const Singleton&)=delete;
protected:
Singleton() {
std::cout<<"constructor called!"<<std::endl;
}
};
/********************************************/
// Example:
// 1.friend class declaration is requiered!
// 2.constructor should be private
class DerivedSingle : public Singleton<DerivedSingle> {
// !!!! attention!!!
// needs to be friend in order to
// access the private constructor/destructor
friend class Singleton<DerivedSingle>;
private:
DerivedSingle() = default;
};
int main(int argc, char* argv[]){
DerivedSingle& instance1 = DerivedSingle::Instance();
DerivedSingle& instance2 = DerivedSingle::Instance();
return 0;
}
该模式的思想是,通过模板类的静态成员变量来确保一个类只有一个实例,并且可以通过静态函数来获取该实例。在这种模式下,我们将 Singleton
类作为基类,派生出一个具体的单例类(例如 MySingleton
),并让 MySingleton
类继承自 Singleton<MySingleton>
。
这个单例模式有非常多的实现细节需要注意,足以考察你的C++功底。
首先Singleton的构造是protected的,因为Singleton本身只是个帮助类,并没有单独实例化的需要,但是子类需要实例化,所以需要protected子类才可以访问。
在 Singleton
类中,我们定义了一个 Instance()
静态函数,返回一个类型为 T&
的对象。在 Instance()
函数中,我们定义了一个静态局部变量 instance
,用于存储 T
类型的唯一实例。由于静态局部变量的生命周期与程序的运行周期相同,因此 nstance
只会在程序第一次调用 Instance()
函数时被创建,并在程序结束时被销毁。通过返回 instance
的引用,我们可以保证每次调用 Instance()
函数时都返回同一个实例。
此外,我们在 Singleton
类删除拷贝构造和赋值运算符的语句,以确保单例对象不能被复制或赋值,并且能够正确释放资源。
在这里基类的析构函数可以不需要 virtual ,因为子类在应用中只会用 Derived 类型,保证了析构时和构造时的类型一致
在 MySingleton
类中,我们只需要简单地继承自 Singleton<MySingleton>
,并在构造函数中添加一些特定的逻辑即可。由于 MySingleton
类已经继承自 Singleton<MySingleton>
,因此可以通过调用 Singleton<MySingleton>::Instance()
函数来获取唯一的 MySingleton
实例。
这种使用 CRTP 实现的单例模式具有以下优点:
Instance()
函数时创建实例,因此该模式对性能的影响较小这里也有几个特殊的限制: