行业知识
C++单例模式、工厂模式
2026-11-14 13:44:00
来源:互联网转载
一、单例模式
(一) 什么是单例模式
1. 是什么?
在���统的整个生命周期内,一个类只允许存在一个实例。
2. 为什么?
两个原因:
- 节省资源。
- 方便控制,在操作公共资源的场景时,避免了多个对象引起的复杂操作。
3. 线程安全
在单例模式的实现时,需要考虑线程安全问题。
什么是线程安全?
- 多线程并行执行共享数据,线程安全的代码是可以通过同步机制来保证各个线程正常且正确地执行,不会产生数据的污染。
如何保证线程安全?
- 加锁,保证资源每时每刻只被一个线程占用。
- 让线程拥有资源,不用去共享。如:threadlocal可以为每一个线程维护一个私有的本地变量。
4. 单例模式分类
- 懒汉式
系统运行中,实例并不存在,只有当需要使用时,才去创建。需要考虑线程安全。
- 饿汉式(未雨绸缪)
系统一运行,就初始化创建实例,当需要时,直接调用。这样是线程安全的。
5. 单例类的特点
- 构造函数和析构函数都是私有,防止外部构造和析构
- 拷贝构造和赋值构造都是私有,防止外部拷贝和赋值
- 一个静态的获取实例的方法,可以全局访问
(二) 单例模式实现
1. 加锁的懒汉式单例(线程安全)
#include #include // 加锁的懒汉单例模式class SingleInstance{public: // 获取实例 static SingleInstance *getInstance(); // 进程退出时,销毁实例 static void deleteInstance(); // 打印实例地址 void printAddr() { std::cout Show(); // 释放资源 delete pAdidasShoes; pAdidasShoes = NULL; } return 0;}2. 工厂方法模式
每种产品都有独立的生产线
核心组件:
- 抽象工厂类:用于继承的“大类”。
- 具体工厂类:继承于抽象工厂,实现创建对应具体产品对象的方法。
- 抽象产品类:用于继承的“大类”。
- 具体产品类:具体工厂创建的具体产品对象。
工厂方法模式的特点:
- 抽象的工厂类(“大类”)。
工厂方法模式的缺陷:
- 每多一个产品,就需要增加一个具体工厂(“生产线”),相比简单工厂模式,需要定义更多类。
- 一个工厂只能生产一种产品。
工厂方法模式的代码:
// 总鞋厂class ShoesFactory{public: virtual Shoes *CreateShoes() = 0; virtual ~ShoesFactory() {}};// 耐克生产者/生产链class NiKeProducer : public ShoesFactory{public: Shoes *CreateShoes() { return new NiKeShoes(); }};// 阿迪达斯生产者/生产链class AdidasProducer : public ShoesFactory{public: Shoes *CreateShoes() { return new AdidasShoes(); }};// 李宁生产者/生产链class LiNingProducer : public ShoesFactory{public: Shoes *CreateShoes() { return new LiNingShoes(); }};main函数执行:
int main(){ // 鞋厂开设耐克生产线 ShoesFactory *niKeProducer = new NiKeProducer(); // 耐克生产线产出球鞋 Shoes *nikeShoes = niKeProducer->CreateShoes(); // 耐克球鞋广告 nikeShoes->Show(); // 释放资源 delete nikeShoes; delete niKeProducer; // 鞋厂开设阿迪达斯生产者 ShoesFactory *adidasProducer = new AdidasProducer(); // 阿迪达斯生产线产出球鞋 Shoes *adidasShoes = adidasProducer->CreateShoes(); // 阿迪达斯球鞋广告 adidasShoes->Show(); // 释放资源 delete adidasShoes; delete adidasProducer; return 0;}3. 抽象工厂模式
产品不止一类,比如上面提到的鞋厂也要生产运动服。
核心组件:和工厂方法模式一样
抽象工厂模式的特点:
- 一个接口,可以创建多个产品族中的产品对象。如上图,耐克工厂->耐克衣服、耐克鞋子。
抽象工厂模式的缺陷:和工厂方法模式一样,新增产品必须添加具体工厂类
抽象工厂模式代码:
// 基类 衣服class Clothe{public: virtual void Show() = 0; virtual ~Clothe() {}};// 耐克衣服class NiKeClothe : public Clothe{public: void Show() {}};// 基类 鞋子class Shoes{public: virtual void Show() = 0; virtual ~Shoes() {}};// 耐克鞋子class NiKeShoes : public Shoes{public: void Show() {}};// 总厂class Factory{public: virtual Shoes *CreateShoes() = 0;virtual Clothe *CreateClothe() = 0; virtual ~Factory() {}};// 耐克生产者/生产链class NiKeProducer : public Factory{public: Shoes *CreateShoes() { return new NiKeShoes(); }Clothe *CreateClothe() { return new NiKeClothe(); }};main函数执行:
int main(){ // 鞋厂开设耐克生产线 Factory *niKeProducer = new NiKeProducer(); // 耐克生产线产出球鞋 Shoes *nikeShoes = niKeProducer->CreateShoes(); // 耐克生产线产出衣服 Clothe *nikeClothe = niKeProducer->CreateClothe(); // 耐克球鞋广告 nikeShoes->Show(); // 耐克衣服广告 nikeClothe->Show(); // 释放资源 delete nikeShoes; delete nikeClothe; delete niKeProducer; return 0;}(三) 封装性更好的工厂模式
1. 模板工厂
对于工厂方法模式,模板编程,把工厂类改为模板工厂类。
模板工厂模式代码:
产品类:
// 基类 鞋子class Shoes{public:virtual void Show() = 0;virtual ~Shoes() {}};// 耐克鞋子class NiKeShoes : public Shoes{public:void Show() {}};// 基类 衣服class Clothe{public:virtual void Show() = 0;virtual ~Clothe() {}};// 优衣库衣服class UniqloClothe : public Clothe{public:void Show() {}};模板的工厂类:
// 抽象模板工厂类// 模板参数:AbstractProduct_t 产品抽象类template class AbstractFactory{public: virtual AbstractProduct_t *CreateProduct() = 0; virtual ~AbstractFactory() {}};// 具体模板工厂类// 模板参数:AbstractProduct_t 产品抽象类,ConcreteProduct_t 产品具体类template class ConcreteFactory : public AbstractFactory{public: AbstractProduct_t *CreateProduct() { return new ConcreteProduct_t(); }};2. 产品注册模板类 + 单例工厂模板类
在模板工厂中,对工厂类进行了模板编程的封装。不过,对于产品类,还是缺少一个可以统一随时随地获取指定产品对对象的类。
对此,我们可以把产品注册的对象用map保存起来。
// 基类,产品注册模板接口类// 模板参数 ProductType_t 表示的类是产品抽象类template class IProductRegistrar{public: // 获取产品对象抽象接口 virtual ProductType_t *CreateProduct() = 0;protected: // 禁止外部构造和虚构, 子类的"内部"的其他函数可以调用 IProductRegistrar() {} virtual ~IProductRegistrar() {}private: // 禁止外部拷贝和赋值操作 IProductRegistrar(const IProductRegistrar &); const IProductRegistrar &operator=(const IProductRegistrar &);};// 工厂模板类,用于获取和注册产品对象// 模板参数 ProductType_t 表示的类是产品抽象类template class ProductFactory{public: // 获取工厂单例,工厂的实例是唯一的 static ProductFactory &Instance() { static ProductFactory instance; return instance; } // 产品注册 void RegisterProduct(IProductRegistrar *registrar, std::string name) { m_ProductRegistry[name] = registrar; } // 根据名字name,获取对应具体的产品对象 ProductType_t *GetProduct(std::string name) { // 从map找到已经注册过的产品,并返回产品对象 if (m_ProductRegistry.find(name) != m_ProductRegistry.end()) { return m_ProductRegistry[name]->CreateProduct(); } // 未注册的产品,则报错未找到 std::cout
- 一个接口,可以创建多个产品族中的产品对象。如上图,耐克工厂->耐克衣服、耐克鞋子。
- 饿汉式(未雨绸缪)
- 懒汉式
- 多线程并行执行共享数据,线程安全的代码是可以通过同步机制来保证各个线程正常且正确地执行,不会产生数据的污染。