文章目录
一、单例模式的概念
(一)基础概念
【1. 概念】
单例模式Singleton:单个对象的模式。目的:类只能实例化一个对象
【2. 单例模式的分类】
- 懒汉模式:延时加载,唯一对象使用时才加载。
- 饿汉模式:贪婪加载,在类加载时就生成唯一的对象,线程安全的单例模式。
【3. 饿汉模式的特点:】
优点:
- 线程安全; 在产生线程之前就完成唯一对象的创建,所以不会存在线程不安全问题。
- 运行时获取对象速度快;因为在类加载时已经创建,运行只用获取即可。
缺点:
- 存在资源浪费的可能;如果不需要生成唯一的对象,只是加载了该类,还是会创建一个对象,资源效率不高。
- 类加载时速度慢;需要创建对象。
【4. 懒汉模式的特点:】
优点:
- 资源利用率高;只有调用该类的公有接口时才会创建唯一的对象。
- 加载类速度快。
缺点:
- 存在线程安全问题,需要引入同步机制;如果程序在多线程情况运行,创建唯一对象的操作不是原子操作,所以就存在临界资源被多个线程访问抢占的情况出现,导致线程不安全问题,所以需要引入同步机制,如互斥锁,信号量等来控制唯一对象的生成。
- 运行获取速度慢, 多线程同步机制带来额外开销。
所以建议使用饿汉模式,可以减少额外的同步开销。
(二)单例模式的设计思想
【 一、思想切入点:】
需要生成唯一的对象,故了解如何生成一个对象,然后对其进行控制,达到只创建一个对象的目的。
对象的生成需要两步:
- 开辟对象所占的内存空间—系统开辟,我们无法控制
- 调用构造函数—自己写的,所以要只生成一个对象,可以对其进行控制。
- 拷贝构造函数也可以生成对象,那么也要对它进行控制。
【二、 根据切入点,进行具体函数设计】
【1. 将生成对象的接口屏蔽】
- 对构造函数进行控制:如果构造函数访问限定符为公有的,那么任意位置都可以访问,就代表任意位置调用构造函数生成对象,无法控制对象的个数。所以第一步就是屏蔽对象生成的接口,将其写为私有的,只能在本类类中访问,在类外无法调用。无法生成对象,那么就可以控制对象生成的个数。构造函数内部就实现对成员变量的初始化即可。
- 对拷贝构造函数进行控制,将其也写为私有,但是不需要进行具体的实现,因为调用拷贝构造的作用是,用已有的对象生成一个新对象。要生成唯一的一个对象,肯定不能调用它,所以只需将它声明为私有的,告诉编译器这个是私有的,不可以用。
【2. 提供接口在类中生成唯一对象】
类外无法调用,那么我们只要在类中生成唯一的对象,然后供外部使用即可。所以我们在类中提供一个接口,然后返回出去供外部使用。通过这个接口得到对象,进行使用
【2-1 函数返回值设计】
需要返回一个对象,无非就三种情况:
- 类名:所有类类型的返回值都是由临时对象带出的,那么就存在临时对象,现在就有两个对象,一个生成的,一个临时对象,和设计原则不符,错误。
- 类名&:无临时对象生成
- 类名*:无临时对象生成
所以返回值可以使用指针或引用。
【2-2 函数修饰关键字的考虑–从如何调用的角度入手】
如何调用这个公有的成员方法来生成唯一的对象?
如果依赖对象调用,那么需要通过对象调用接口,但是调用它了才会生成对象,形成了一种相互依赖关系,那么这第一个对象要如何生成呢?这就和鸡生蛋,蛋生鸡的问题一样了。所以如果依赖对象调用,那么第一个对象永远无法生成。
所以必须将此函数设计为不依赖对象调用,那么就需要将此函数写为静态函数,不依赖对象调度,通过类作用域就可以调用。所以当一个接口目的是为了生成对象,一般都写为静态的。
【3. 标识唯一的对象】
为了防止这个公有的接口被调用多次,需要定义一个标识唯一对象的指针, 这个指针不属于对象,不依赖对象调动,只能让本类中的接口函数调动,故将其还是定义为静态变量,在类外进行初始化,初始化为NULL。
- 只要为空,就表示唯一的对象还没有生成,允许调用构造函数生成对象。
- 如果不为空,那么就直接返回这个对象的地址即可。
总结单例模式设计点就三大点:
- 屏蔽生成对象的接口;构造函数,拷贝构造函数访问限定符为私有private。
- 通过接口来生成唯一的对象;不能以类类型返回,用static关键字修饰。
- 定义指针指向唯一的对象,用来标识唯一的对象。
二、单例模式之懒汉模式
(一)线程不安全的懒汉模式
根据单例模式的设计思路,我们可以写出懒汉模式的单例模式:
在公有的成员方法中调用构造函数生成对象,这样只有调用公有接口才会生成对象。那么代码如下:
# include<iostream>
/*
//1.懒汉模式*/