单例模式:
是一种创建型设计模式,在日常的开发中运用比较广泛,存在某一种类,我们只希望整个模块中每次只存在这个类的一个实例,下面举几个例子:
- windows的任务管理器;
- 系统日志模块,我们不希望存在太多的实例,往往希望将宝贵的系统资源交给更重要的模块,不需要日志模块执行有多快多好,只要最终能把log记录即可,提高系统性能,提升用户体验;
- 数据库连接,如果每个用户来访问,都给创建一次数据库连接实例,这样会使得服务器充斥着大量的这种实例,过多的占用宝贵资源,而是希望将更多的资源来提升用户体验
- …
如何设计一个单例模式:
class Singleton{
public:
static Singleton *GetInstance(){ //用户获取对象的唯一途径
return &instance;
}
private:
static Singleton instance;
Singleton(){}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::instance;
过程总结为以下几点:
- 构造函数和赋值重载私有化,束缚用户自行创建对象;
- 定义该类型唯一的静态对象;
- 通过一个static静态成员方法返回唯一的对象实例。
- 这个instance对象,理解为类作用域下的静态数据,是在main()函数前就会被创建出来,在内存中只存在一份,其实这也解释了为什么被称为"饿汉"的原因,用户没有自己去获取,这个对象已经在内存在中创建出来了;
- 其他用户,或者其他线程去访问获取实例,也都获取到的是这个instance,所以饿汉单例必然是线程安全的单例。
使用:
int main(){
Singleton *ptr1 = Singleton::GetInstance();
Singleton *ptr2 = Singleton::GetInstance();
std::cout << ptr1 << std::endl;
std::cout << ptr2 << std::endl;
return 0;
}
编译链接运行后:
001CF322
001CF322
总结一下:
- 饿汉:因为程序还没运行起来,该实例就已经存在了;
- 线程安全:是因为实例不是有线程创建的,是有系统创建的,存在于进程的.data段,所有线程创建必然是在实例产生之后的,所以不用担心产生竞争条件;
- 静态成员可以理解为类作用域下的全局变量,由静态方法访问,只要这样不需要依赖对象。
- 饿汉缺点:如果系统中存在大量的饿汉单例或者静态数据,必然会使得系统启动变慢,因为需要大量数据需要创建和初始化。
饿汉:用户/线程调用GetInstance()时,对象已经初始化好了,只需要返回对象地址即可;
懒汉:用户/线程第一次调用GetInstance()时,才回去创建这个对象。