保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:
- 当一个类只有一个实例,而且客户端可以从一个众所周知的访问点访问它时;
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无须更改代码就能使用一个扩展的实例时;
例子:
1. 常用单例
class Singleton
{
public:
static Singleton* getSingleton()
{
if(singleton == nullptr)
{
singleton = new Singleton();
}
return singleton;
}
void show() { cout << "Singleton" << endl; }
private:
static Singleton *singleton;
Singleton(){}
Singleton(Singleton&) = delete;
Singleton& operator=(Singleton&) = delete;
};
Singleton *Singleton::singleton = nullptr;
简单版的单例模式实现方式是通过编译时创建一个静态指针,调用时new创建一个动态对象,然后返回该静态指针实现对象获取,为了保证全局唯一,需要将构造函数变为私有,并且禁止拷贝构造和赋值函数。
2. 模板单例模式
template<class T>
class Singleton
{
public:
static T& getSingleton()
{
static T singleton; //多次调用只会实例一次
return singleton;
}
protected:
Singleton() { cout << "Singleton::Singleton()" << endl; } //为了派生类继承后,实例的时候能够调用构造函数
private:
Singleton(Singleton&) = delete;
Singleton& operator=(Singleton&) = delete;
};
class A : public Singleton<A>
{
public:
friend class Singleton<A>; //为了基类能够调用派生类private成员函数和变量
void showA() { cout << "A::showA()" << endl; }
private:
A(A&) = delete;
A& operator=(A&) = default;
A():Singleton<A>(){ cout << "A::A()" << endl; };
};
void doSingletonPattern()
{
A &a1 = Singleton<A>::getSingleton();
A &a2 = Singleton<A>::getSingleton();
a1.showA();
a2.showA();
cout << "a1==" << &a1 << "\ta2==" << &a2 << endl;
}
模板版单例模式是通过提取简单版单例模式公共部分总结成一个框架,后面任何继承该基类的类都是单例模式,实现了单例模式的模板化。实现要点是在返回函数内,通过创建一个静态指针并且指向一个new创建的对象空间,然后返回该对象指针。由于在基类实例化派生类对象,故基类需要调用派生类的构造函数,由于派生类构造函数是私有的,所以需要在派生类中将该基类声明为私有的;同时派生类调用构造函数的时候,需要调用基类构造函数,所以基类构造函数必须要声明为受保护的类型以便被派生类继承和调用;
简单版和模板版单例模式差异:
两者主要区别在实例化对象方式上。简单版直接实例化对象然后返回该静态指针,直接调用该返回的指针即可;而模板版则是在基类模板实例化派生类(派生类将自己的类型传入基类),由于基类要调用派生类构造函数,所以需要在派生类中将基类声明为友元类;而派生类调用构造函数时,需要先调用基类构造函数,故需要继承基类构造函数,所以基类构造函数必须声明为protected。
总之一个主要逻辑就是:基类实例化派生类对象——>基类需要调用派生类构造函数(派生类中将基类声明为友元类)——>派生类要先调用基类构造函数(基类构造函数声明为protected)