C++设计模式总结

网友投稿 297 2022-11-01


C++设计模式总结

文章目录

​​一、单例模式​​​​二、简单工厂 Simple Factory​​​​三、工厂方法 Factory Method​​​​四、抽象工厂 Abstract Factory​​​​五、代理模式​​​​六、装饰器模式 Decorator​​​​七、适配器模式​​​​八、观察者—监听者模式​​

一、单例模式

一个类不管常见多少次对象,永远只能得到该类型一个对象的实例

// p1、p2、p3指向同一对象A* p1 = new A();A* p2 = new A();A* p3 = new A();

饿汉式单例模式: 还没有获取实例对象,实例对象就已经产生了

优点:线程安全。对象存放在数据段,main函数还没有开始执行,对象就已经初始化好了,一定是线程安全的。

缺点: 获取在软件启动的时候,并没有使用到这个对象,然而这个对象已经产生,这就比较浪费资源。

我们要限制类对象的个数,就要限制构造对象的方法,即构造函数。不能让外界随意访问我们的构造函数,所以我们需要构造函数私有化,调用构造函数的必须是用户调用的某个接口,我们把接口写好,就能控制外界访问构造函数的方式。

单例模式的核心是构造方法的私有化(即在入口处限制了对象的实例化),之后在类的内部实例化对象,并通过静态方法返回实例化对象的引用

为什么获取唯一的实例对象的getInstance要定义成static?

由于普通成员方法的调用依赖对象,而获取唯一对象的时候并没有产生对象,所以定义成static

为什么唯一的实例对象是static?

存放在数据段,不占用对象空间。如果不写成static,会引起类的循环定义。

懒汉式单例模式(多用): 唯一的实例对象是在第一次获取的时候才产生

class Singleton {public: static Singleton* getInstance() { if (instance == nullptr) { instance = new Singleton();// 堆区 } return instance; }private: // 定义一个唯一的实例对象的指针(数据段) static Singleton* volatile instance; Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};Singleton* volatile Singleton::instance = nullptr;

getInstance是否是可重入函数(非递归环境下)?

可重入函数: 主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的

​​instance = new Singleton()​​被拆解为:开辟内存、构造对象、给instance赋值。只要还没给instance赋值,if条件满足,另一线程就可以进入if语句,线程不安全,不是可重入函数。

通过 锁 + 双重判断 的方法,改为线程安全的函数

static Singleton* getInstance() { if (instance == nullptr) { unique_lock lock(mtx); if (instance == nullptr) { instance = new Singleton();// 堆区 } } return instance; }

​​instance​​​指针属于数据段,是同一进程多个进程共享的内存。 为了加快指令的执行,CPU会把共享内存的值拷贝一份带到各个线程的缓存,这依然导致线程不安全,所以加上​​​volatile​​,使得各个线程不再持有缓存

更为简洁的线程安全写法

class Singleton {public: static Singleton* getInstance() { // 函数局部静态变量的初始化,在汇编指令上已经自动添加线程互斥的指令 static Singleton instance; return &instance; }private: Singleton() { } Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete;};

二、简单工厂 Simple Factory

简单工厂(Simple Factory)不属于标准的OOP设计模式中的一项,在C++项目里会出现很多类,每次创建对象的时候,都需要通过new 类名称的方式来生成对象,用户需要记忆很多类的名称,这样的设计使得代码很难维护,类名如果做了改变,那么所有使用类名称的地方都需要去修改,耦合性太强,Simple Factory就此诞生的。

#include#include#includeusing namespace std; class Car {public: Car(string name) :_name(name) { } // 纯虚函数 virtual void show() = 0; string _name;};class Bmw : public Car {public: Bmw(string name) : Car(name) { } void show() { cout << "获取一辆BMW " <<_name<< endl; }};class Benz : public Car {public: Benz(string name) : Car(name) { } void show() { cout << "获取一辆Benz " << _name << endl; }};enum CarType { BMW, BENZ};class SimpleFactory {public: Car* create_car(CarType ct) { switch (ct) { case BMW: return new Bmw("X5"); case BENZ: return new Benz("A6"); default: cerr << "参数有误" << endl; break; } return nullptr; }};int main(){ unique_ptr factory(new SimpleFactory()); unique_ptr p1(factory->create_car(BMW)); unique_ptr p2(factory->create_car(BENZ)); p1->show(); p2->show(); return 0;}

create_car函数无法做到 “开-闭”原则 ,即对原有代码修改关闭,对功能扩展开放。这个函数随着新对象的添加,或者原有对象的删除,都会导致该函数的代码修改,而且有可能影响原来的功能。

三、工厂方法 Factory Method

工厂方法为每种产品都提供了相应的实例工厂,当增加新的汽车工厂时,只需要增加对应工厂类即可,删除汽车工厂时,则只需要删除工厂类。

可解决简单工厂不符合 “开-闭”原则 的问题。

class Benz : public Car {public: Benz(string name) : Car(name) { } void show() { cout << "获取一辆Benz " << _name << endl; }};class Factory {public: // 工厂方法 virtual Car* create_car(string name) = 0;};// 宝马工厂class BmwFactory : Factory{public: Car* create_car(string name) { return new Bmw(name); }};// 奔驰工厂class BenzFactory : Factory {public: Car* create_car(string name) { return new Benz(name); }};

增加main函数

int main(){ // 创建工厂 unique_ptr bmw_factory(new BmwFactory()); unique_ptr benz_factory(new BenzFactory()); // 给型号即可拿到车,无需手动new创建 unique_ptr p1(bmw_factory->create_car("X5")); unique_ptr p2(benz_factory->create_car("A6")); p1->show(); p2->show(); return 0;}

缺点: 每一个实例工厂负责生产一种实例产品,也就是一个产品对应一个工厂,一个工厂对应一个产品。在现实生活中一个工厂是可以生产多种产品的,而工厂方法中,给这每一个产品都创建一个工厂类,这样代码中的类就太多了,不好维护。

四、抽象工厂 Abstract Factory

抽象工厂用于生产有关联关系的系列产品

车产品类

class Car {public: Car(string name) :_name(name) { } // 纯虚函数 virtual void show() = 0; string _name;};class Bmw : public Car {public: Bmw(string name) : Car(name) { } void show() { cout << "获取一辆BMW " <<_name<< endl; }};class Benz : public Car {public: Benz(string name) : Car(name) { } void show() { cout << "获取一辆Benz " << _name << endl; }};

灯产品类

class Light {public: virtual void show() = 0;};class BmwLight : public Light{public: void show() { cout << "得到一个BMW车灯" << endl; }};class BenzLight : public Light {public: void show() { cout << "得到一个Benz车灯" << endl; }};

工厂类

抽象工厂:对有一组关联关系的产品簇提供统一创建

class AbstractFactory {public: // 工厂方法 创建汽车 virtual Car* create_car(string name) = 0; // 工厂方法 创建车灯 virtual Light* create_car_light() = 0;};// 宝马工厂class BmwFactory : public AbstractFactory {public: Car* create_car(string name) { return new Bmw(name); } Light* create_car_light() { return new BmwLight(); }};// 宝马工厂class BenzFactory : public AbstractFactory {public: Car* create_car(string name) { return new Benz(name); } Light* create_car_light() { return new BenzLight(); }};

int main(){ // 创建抽象工厂,可生产有关联关系的一类产品 unique_ptr bmw_factory(new BmwFactory()); unique_ptr benz_factory(new BenzFactory()); unique_ptr c1(bmw_factory->create_car("X5")); unique_ptr c2(benz_factory->create_car("A6")); unique_ptr l1(bmw_factory->create_car_light()); unique_ptr l2(benz_factory->create_car_light()); c1->show(); c2->show(); l1->show(); l2->show(); return 0;}

简单工厂 Simple Factory

把对象的创建封装在一个接口函数里面,通过传入不同的标识,返回不同的对象。客户不用自己手动new对象,不用了解对象的创建过程。

提供创建对象实例的接口函数不闭合,不能对修改关闭

工厂方法 Factory Method

工厂基类,提供了纯虚函数用于提供创建产品的接口,派生类重写创建产品的接口。可以做到不同的产品在不同的工厂里创建,模块化清晰。

可以做到对现有工厂以及产品的修改关闭。

实际上,很多有关联关系的产品是放在同一工厂生产的。然而这种方法中,一种产品对应一个工厂,粒度太小,工厂类过多,维护困难。

抽象工厂 Abstract Factory

把有关联关系的产品放在一个抽象工厂内进行生产,派生类重写接口后实例化工厂,用于生产产品。

五、代理模式

最简单的作用就是对用户进行权限校验,然后访问委托类

委托类

class VideoSite {public: virtual void free_video() = 0; virtual void vip_video() = 0; virtual void ticket_video() = 0;};// 委托类class FixBugVideoSite : public VideoSite {public: void free_video() {cout << "观看免费电影" << endl;} void vip_video() {cout << "观看vip电影" << endl;} void ticket_video() {cout << "观看用券电影" << endl;}};

代理类

// 用代理类进行权限控制// 免费电影代理类class FreeVideoSiteProxy : public VideoSite {public: FreeVideoSiteProxy() { // 对对象进行代理 p_video = new FixBugVideoSite(); } ~FreeVideoSiteProxy(){delete p_video;} void free_video() {p_video->free_video();} void vip_video() {cout << "需要成为vip,才可观看vip电影" << endl;} void ticket_video() {cout << "需要充值电影券,才可观看此电影" << endl;}private: VideoSite* p_video;};class VipVideoSiteProxy : public VideoSite {public: VipVideoSiteProxy() { // 对对象进行代理 p_video = new FixBugVideoSite(); } ~VipVideoSiteProxy() {delete p_video;} void free_video() {p_video->free_video();} void vip_video() {p_video->vip_video();} void ticket_video() {cout << "需要充值电影券,才可观看此电影" << endl;}private: // 基类指针访问派生类对象 VideoSite* p_video;};

int main() { // 免费代理类,多态:基类指针指向派生类对象 unique_ptr p1(new FreeVideoSiteProxy()); p1->free_video(); p1->vip_video(); p1->ticket_video(); unique_ptr p2(new VipVideoSiteProxy()); p2->free_video(); p2->vip_video(); p2->ticket_video(); return 0;}

六、装饰器模式 Decorator

可用于现有类的功能扩充

一般而言,我们扩充功能一般采用子类重写接口的方式,每个类都要添加代码,这样会导致添加过多的代码加入类

使用装饰器进行功能扩充

车类

// 抽象基类class Car {public: virtual void show() = 0;};class BMW : public Car {public: void show() { cout << "这是一辆宝马,有基本配置"; }};class Benz : public Car {public: void show() { cout << "这是一辆奔驰,有基本配置"; }};

装饰器类

// 装饰器的基类class CarDecorator : public Car {public: // 对p进行装饰,Car*表示基类指针 CarDecorator(Car* p) :p_car(p){} // 用基类指针保留装饰的对象 Car* p_car;};// 功能类1class FunDecorator1 : public CarDecorator {public: FunDecorator1(Car* p): CarDecorator(p) {} void show() { p_car->show(); // 添加新功能 cout << ",自动刹车"; }};// 功能类2class FunDecorator2 : public CarDecorator {public: FunDecorator2(Car* p) : CarDecorator(p) {} void show() { p_car->show(); // 添加新功能 cout << ",定速巡航"; }};

装饰器使用

int main() { // 由于FunDecorator1和FunDecorator2的构造函数接收的都是基类的指针类型 // 所以直接嵌套装饰 unique_ptr bmw(new FunDecorator2(new FunDecorator1(new BMW()))); bmw->show(); cout << endl; unique_ptr benz(new FunDecorator2(new FunDecorator1(new Benz()))); benz->show(); cout << endl; return 0;} /*这是一辆宝马,有基本配置,自动刹车,定速巡航这是一辆奔驰,有基本配置,自动刹车,定速巡航*/

七、适配器模式

class VGA {public: virtual void play() = 0;};// 只支持VGA接口的投影仪class Projector_VGA : public VGA{public: void play() { cout << "投影仪通过VGA接口进行视频播放" << endl; }};// 只支持VGA接口的电脑class Computer {public: void play_video(VGA* p_vga) { cout << "电脑连接VGA接口的投影仪" << endl; p_vga->play(); }};int main() { Computer().play_video(new Projector_VGA()); return 0;}/*电脑连接VGA接口的设备投影仪通过VGA接口进行视频播放*/

现在有一批新的投影仪,这些投影仪只支持HDMI接口

class HDMI {public: virtual void play() = 0;};// 只支持VGA接口的投影仪class Projector_HDMI : public HDMI {public: void play() { cout << "投影仪通过HDMI接口进行视频播放" << endl; }};

这时需要用只支持VGA的旧电脑连接HDMI接口的投影仪,就需要用得到适配器,这个适配器相当于一个转换头,把VGA信号转成HDMI信号

// 添加适配器,使得VGA接口的电脑和HDMI接口的投影仪一起工作class VGAToHDMIAdapter : public VGA{public: VGAToHDMIAdapter(HDMI* p) :p_hdmi(p) { } // 重写VGA的play方法,用VGA的play封装HDMI的play(转换器方法) void play() { // 此处调用的是HDMI接口的 cout << "使用转换器将VGA信号转为HDMI信号" << endl; p_hdmi->play(); }private: HDMI* p_hdmi;};

int main() { Computer().play_video(new VGAToHDMIAdapter(new Projector_HDMI())); return 0;}

八、观察者—监听者模式

也叫发布—订阅模式,主要关注的是对象一对多的关系。也就是多个对象依赖一个对象,当该对象状态发生变化时,其他对象也应该及时收到通知。

就好比有一个数据对象,一个折线图对象,一个柱状图对象,一个圆饼图对象。当数据对象发生变化时,其他三个对象都要发生相应的变化。

3个订阅者

class Observer {public: virtual void handle(int msg_id) = 0;};class Observer1 : public Observer {public: void handle(int msg_id) { switch (msg_id) { case 1: cout << "Observer1 recv msg 1" << endl; break; case 2: cout << "Observer1 recv msg 2" << endl; break; default: cout << "Observer1 recv unknown msg" << endl; break; } }};class Observer2 : public Observer {public: void handle(int msg_id) { switch (msg_id) { case 2: cout << "Observer2 recv msg 2" << endl; break; default: cout << "Observer2 recv unknown msg" << endl; break; } }};class Observer3 : public Observer {public: void handle(int msg_id) { switch (msg_id) { case 1: cout << "Observer3 recv msg 1" << endl; break; case 3: cout << "Observer3 recv msg 3" << endl; break; default: cout << "Observer3 recv unknown msg" << endl; break; } }};

1个发布者

// 主题类class Subject {public: void add_observer(int msg_id, Observer* observer) { _sub_map[msg_id].push_back(observer); /* auto iter = _sub_map.find(msg_id); if (iter != _sub_map.end()) { iter->second.push_back(observer); } else { list li; li.push_back(observer); _sub_map.insert({ msg_id, li }); }*/ } // 通知对msg_id感兴趣的观察者处理该事件 void dispatch(int msg_id) { auto iter = _sub_map.find(msg_id); if (iter != _sub_map.end()) { for (Observer* obser : iter->second) { obser->handle(msg_id); } } }private: // 存放对事件感兴趣的观察者们 unordered_map> _sub_map;};

测试代码

int main() { Subject subject; Observer* obser1 = new Observer1(); Observer* obser2 = new Observer2(); Observer* obser3 = new Observer3(); // 给订阅者添加对指定事件感兴趣的观察者 subject.add_observer(1, obser1); subject.add_observer(2, obser1); subject.add_observer(2, obser2); subject.add_observer(1, obser3); subject.add_observer(3, obser3); int msg_id; while (true) { cout << "输入消息类型:"; cin >> msg_id; if (msg_id == -1) { break; } subject.dispatch(msg_id); } return 0;}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【复习笔记】操作系统之进程的同步和互斥
下一篇:Java的Synchronized关键字学习指南(全面 & 详细)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~