1.什么是单例模式?
单例模式(Singleton Pattern)是一种创建型设计模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
在单例模式中,类的构造函数被私有化,以防止其他对象直接通过 new
关键字创建实例。通过定义一个静态方法或静态属性来访问单例对象,其他对象可以通过该方法获取单例的引用。
2.单例模式的好处
-
全局访问点:单例模式提供了一个全局访问点,使得其他对象可以方便地获取单例对象的引用。这消除了全局变量的需要,并提供了一种统一的访问方式,使得对象的使用变得简单和一致。
-
单一实例:单例模式确保一个类只有一个实例存在。这对于某些资源密集型的对象(如数据库连接、线程池、缓存等)非常有用,因为多个实例可能会导致资源浪费和冲突。通过单例模式,可以节省系统资源并提高性能。
-
避免不一致状态:由于单例模式只有一个实例存在,可以避免多个实例之间的状态不一致问题。多个实例之间的状态不一致可能导致系统行为异常或数据不一致,而单例模式可以确保对象状态的一致性。
-
延迟加载:单例模式可以实现延迟加载(也称为懒加载),即只在需要时才创建单例对象。这对于资源消耗较大的对象或需要耗费较长时间进行初始化的对象非常有用。通过延迟加载,可以提高应用程序的启动速度和内存使用效率。
-
控制实例化过程:由于单例模式将对象的实例化过程封装在类内部,其他对象无法直接通过
new
关键字创建实例。这样可以对实例化过程进行严格的控制,例如在实例化时进行一些初始化操作、资源分配等。 -
提供了一种全局状态访问点:单例模式可以用于提供一种全局状态的访问点,使得各个模块可以方便地访问和共享全局状态。这对于需要在不同模块之间共享数据或状态的情况非常有用。
3.如何实现?
实现思路:
- 私有化类的构造函数,以防止外界创建单例类的对象;
- 使用类的私有静态指针变量指向类的唯一实例;
- 提供公有的静态方法获取该实例,拷贝构造函数和重载赋值运算符也应该禁止。
单例模式有两种实现方法,分别是懒汉和饿汉模式。
顾名思义,懒汉模式,即非常懒,不用的时候不去初始化,所以在第一次被使用时才进行初始化;
饿汉模式,即迫不及待,在程序运行时立即初始化。
4.为什么说懒汉式实现方式是线程安全的?
对于local static 对象,其初始化发生在控制流第一次执行到该对象的初始化语句时。多个线程的控制流可能同时到达其初始化语句。
在C++11之前,在多线程环境下local static对象的初始化并不是线程安全的。
具体表现就是:如果一个线程正在执行local static对象的初始化语句但还没有完成初始化,此时若其它线程也执行到该语句,那么这个线程会认为自己是第一次执行该语句并进入该local static对象的构造函数中。这会造成这个local static对象的重复构造,进而产生内存泄露问题。所以,local static对象在多线程环境下的重复构造问题是需要解决的。
而C++11则在语言规范中解决了这个问题。C++11规定,在一个线程开始local static 对象的初始化后到完成初始化前,其他线程执行到这个local static对象的初始化语句就会等待,直到该local static 对象初始化完成。
5.单例模式的应用场景?
-
配置信息类:单例模式可以用于表示应用程序的配置信息,例如数据库连接配置、日志配置等。通过单例模式,可以确保配置信息在整个应用程序中只有一个实例,并且可以方便地被其他对象访问。
-
日志记录器:在许多应用程序中,需要记录日志以进行调试和故障排除。单例模式可以用于创建一个全局的日志记录器,使得所有的日志信息都被集中记录,并可以方便地进行管理和访问。
-
缓存:单例模式可以用于创建缓存对象,用于存储频繁访问的数据或计算结果。通过使用单例模式,可以确保缓存对象在整个应用程序中只有一个实例,并且可以方便地被各个模块共享和使用。
-
线程池:在多线程编程中,线程池用于管理和调度线程的执行。单例模式可以用于创建全局唯一的线程池对象,以确保线程池的统一管理和控制。
-
对话框、窗口管理器:在图形用户界面(GUI)应用程序中,单例模式可以用于管理对话框或窗口对象。通过单例模式,可以确保只有一个对话框或窗口管理器实例存在,以便于对界面进行统一的管理和控制。
-
计数器、计时器:在需要统计某个事件发生的次数或计算某个操作的执行时间等情况下,可以使用单例模式创建一个全局的计数器或计时器对象,方便各个模块进行统计和计时操作。
6.单例模式会带来哪些问题?
单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。