文章目录
- 1. 单例模式 与 设计模式
- 1.1 单例模式
- 1.2 设计模式
- 1.3 饿汉实现模式 与 懒汉实现模式
- 1.4 饿汉模式
- ① 饿汉模式的特点
- ② 饿汉式单例模式的实现
- ③ 饿汉式单例模式的优缺点
- ④ 适用场景
- 1.5 懒汉模式
- ① 懒汉式单例模式的特点
- ② 懒汉式单例模式的实现
- ③ 懒汉式单例模式的优缺点
- ④ 适用场景
- 2. 智能指针 与 线程安全
- 2.1 STL的容器是否是线程安全?
- 2.2 智能指针是否是线程安全?
1. 单例模式 与 设计模式
1.1 单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类在整个应用程序中只有一个实例,并提供一个全局访问点来访问这个实例。这个模式适用于那些只需要一个实例来控制整个系统的场景,如配置管理、日志记录等。
关键特性:
- 唯一性:确保类只有一个实例,避免多实例带来的资源浪费和状态不一致。
- 全局访问:提供一个全局访问点,使得应用程序中的所有代码能够方便地访问这个唯一实例。
- 延迟实例化:实现延迟实例化,即仅在第一次访问时创建实例,从而节省资源并提高启动效率。
典型实现(线程安全的单例模式):
class Singleton {
public:static Singleton& getInstance() {static Singleton instance; // 局部静态变量保证实例唯一性return instance;}// 删除拷贝构造函数和赋值运算符以防止复制Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() {} // 私有构造函数以防止外部创建实例
};
1.2 设计模式
设计模式(Design Pattern)是面向对象设计中的一种标准化、经过验证的解决方案,用于解决常见的设计问题。
设计模式的类型:
-
创建型模式:关注对象的创建过程。常见的有:
- 单例模式(Singleton)
- 工厂模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
- 原型模式(Prototype)
- 建造者模式(Builder)
-
结构型模式:关注类和对象如何组合成更大的结构。常见的有:
- 适配器模式(Adapter)
- 装饰器模式(Decorator)
- 代理模式(Proxy)
- 桥接模式(Bridge)
- 组合模式(Composite)
- 外观模式(Facade)
- 享元模式(Flyweight)
-
行为型模式:关注对象之间的交互和责任分配。常见的有:
- 观察者模式(Observer)
- 策略模式(Strategy)
- 状态模式(State)
- 责任链模式(Chain of Responsibility)
- 迭代器模式(Iterator)
- 访问者模式(Visitor)
- 模板方法模式(Template Method)
- 命令模式(Command)
- 解释器模式(Interpreter)
- 中介者模式(Mediator)
- 备忘录模式(Memento)
设计模式的目的:
- 提高代码复用:通过采用通用的解决方案,减少代码重复,提高复用性。
- 提高代码可维护性:设计模式提供了清晰的结构,使得代码更易于理解和维护,提升了整体可维护性。
- 解决常见问题:设计模式为常见的设计问题提供了经过验证的解决方案,帮助开发人员更高效地解决问题。
1.3 饿汉实现模式 与 懒汉实现模式
单例模式分为饿汉实现 与 懒汉实现两种,简单举个例子:
饿汉方式:吃完饭,立刻洗碗;下一顿吃的时候可以立刻拿着碗就能吃饭.(确保资源随时可以使用)
懒汉方式:吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗(节省了不必要的操作,优化速度)
接下来我们对这两种单例实现模式进行解释:
1.4 饿汉模式
饿汉模式 在类被加载时就立即创建实例,而不是在第一次需要实例时才创建。这种方式的特点是实例创建时机早,通常用于资源不多且创建实例开销较小的情况。
① 饿汉模式的特点
- 立即实例化:类加载时就创建单例实例,不管是否有其他地方需要这个实例。
- 线程安全:由于实例在类加载时就创建好了,线程安全问题得到了天然的解决。
- 浪费资源:如果单例对象的创建开销较大,而实际运行时不一定会使用这个实例,则会浪费资源。
② 饿汉式单例模式的实现
下面是饿汉实现的单例模式的示例( C++ ):
class Singleton {
public:// 提供全局访问点static Singleton& getInstance() {return instance; // 直接返回已经创建好的实例}// 删除拷贝构造函数和赋值运算符以防止复制Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:// 构造函数是私有的,以防止外部创建实例Singleton() {}// 静态成员变量,类加载时就创建实例static Singleton instance;
};// 在类外定义静态成员变量
Singleton Singleton::instance;
③ 饿汉式单例模式的优缺点
优点:
- 线程安全:由于实例在程序启动时即被创建,因此不需要额外的同步机制来确保线程安全。
- 简单易用:实现过程简单,易于理解和使用。
缺点:
- 资源浪费:即使在程序运行过程中不需要这个单例实例,也会在启动时创建,可能导致资源浪费。
- 不适合大开销对象:对于实例化开销较大的对象,预创建可能导致不必要的资源消耗。
④ 适用场景
- 实例化对象开销较小且必定会被使用。
- 对线程安全要求较高,且不介意在应用启动时立即创建实例。
1.5 懒汉模式
懒汉模式(Lazy Initialization) 是一种单例模式的实现方式,它在第一次需要实例时才创建对象。这种方式的特点是推迟实例化,从而避免不必要的资源消耗。
① 懒汉式单例模式的特点
- 延迟实例化:对象仅在第一次被请求时才进行创建,从而提高了程序的效率。
- 资源节省:在不需要实例的情况下,避免了资源的浪费,优化了资源使用。
- 线程安全问题:在多线程环境下,可能需要额外的同步机制来确保线程安全,确保实例的唯一性。
② 懒汉式单例模式的实现
下面是懒汉模式的实现( C++ ):
#include <mutex>class Singleton {
public:// 提供全局访问点static Singleton* getInstance() {if (!instance) { // 如果实例尚未创建std::lock_guard<std::mutex> guard(mutex); // 加锁if (!instance) { // 再次检查以避免多线程下的重复创建instance = new Singleton(); // 创建实例}}return instance;}// 删除拷贝构造函数和赋值运算符以防止复制Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:// 构造函数是私有的,以防止外部创建实例Singleton() {}// 静态成员变量,保存单例实例static Singleton* instance;// 互斥锁,保护实例化过程static std::mutex mutex;
};// 在类外定义静态成员变量
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;
③ 懒汉式单例模式的优缺点
优点:
- 节省资源:实例仅在实际需要时创建,从而避免了不必要的资源消耗。
- 延迟加载:能够提升程序启动速度,尤其在创建单例对象开销较大的情况下尤为显著。
缺点:
- 线程安全问题:在多线程环境下,可能需要额外的同步机制,如使用互斥锁(mutex),以避免竞态条件。
- 实现复杂度:比起饿汉式单例实现更复杂,需要处理多线程环境下的同步问题。
④ 适用场景
- 实例化开销较大或不一定需要使用实例时。
- 需要延迟加载,优化资源使用。
2. 智能指针 与 线程安全
2.1 STL的容器是否是线程安全?
不是!
- STL 的设计初衷是尽可能的利用性能,进行高效的编码,一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响.
- 对于不同的容器,加锁方式的不同,性能可能也不同(如hash表的锁表和锁桶)
- 因此 STL 默认不是线程安全. 如果需要在多线程环境下使用, 往往需要调用者自行保证线程安全
2.2 智能指针是否是线程安全?
- 对于
unique_ptr
,其只在当前代码块范围内生效, 因此不涉及线程安全问题. - 对于
shared_ptr
, 多个对象需要共用一个引用计数变量,存在线程安全问题,但标准库实现时考虑到了该问题,基于原子操作(CAS)的方式保证shared_ptr
能够高效, 原子的操作引用计数