C++设计模式总结

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

// p1、p2、p3指向同一对象
A* p1 = new A();
A* p2 = new A();
A* p3 = new A();
1
2
3
4
饿汉式单例模式: 还没有获取实例对象,实例对象就已经产生了

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

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

#include<iostream>

using namespace std;

class Singleton {
public:
    // 由于普通成员方法的调用依赖对象,而获取唯一对象的时候并没有产生对象,所以定义成static
    // static接口获取指向对象的指针
    static Singleton* getInstance() {
        return &instance;
    }

private:
    // 定义一个唯一的实例对象(数据段)
    static Singleton instance;

    // 构造函数私有化
    Singleton() {

    }

    // delete拷贝构造和赋值重载
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

// 类外初始化static成员变量处于数据段,对象在函数执行前就存在,运行到的时候才初始化
Singleton Singleton::instance;

int main(){
    Singleton* p1 = Singleton::getInstance();
    Singleton* p2 = Singleton::getInstance();
    Singleton* p3 = Singleton::getInstance();
    cout << p1 << endl;
    cout << p2 << endl;
    cout << p3 << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
懒汉式单例模式(多用): 唯一的实例对象是在第一次获取的时候才产生

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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
getInstance是否是可重入函数(非递归环境下)?

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

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

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

static Singleton* getInstance() {
        if (instance == nullptr) {
            unique_lock<mutex> lock(mtx);
            if (instance == nullptr) {
                instance = new Singleton();// 堆区
            }
        }
        return instance;
    }
1
2
3
4
5
6
7
8
9
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;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
二、简单工厂 Simple Factory
简单工厂(Simple Factory)不属于标准的OOP设计模式中的一项,在C++项目里会出现很多类,每次创建对象的时候,都需要通过new 类名称的方式来生成对象,用户需要记忆很多类的名称,这样的设计使得代码很难维护,类名如果做了改变,那么所有使用类名称的地方都需要去修改,耦合性太强,Simple Factory就此诞生的。

#include<iostream>
#include<string>
#include<memory>

using 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<SimpleFactory> factory(new SimpleFactory());
    unique_ptr<Car> p1(factory->create_car(BMW));
    unique_ptr<Car> p2(factory->create_car(BENZ));

    p1->show();
    p2->show();

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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);
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
增加main函数

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

    p1->show();
    p2->show();

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
缺点: 每一个实例工厂负责生产一种实例产品,也就是一个产品对应一个工厂,一个工厂对应一个产品。在现实生活中一个工厂是可以生产多种产品的,而工厂方法中,给这每一个产品都创建一个工厂类,这样代码中的类就太多了,不好维护。

四、抽象工厂 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;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
灯产品类

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;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
工厂类

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

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();
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int main(){
    // 创建抽象工厂,可生产有关联关系的一类产品
    unique_ptr<AbstractFactory> bmw_factory(new BmwFactory());
    unique_ptr<AbstractFactory> benz_factory(new BenzFactory());

    unique_ptr<Car> c1(bmw_factory->create_car("X5"));
    unique_ptr<Car> c2(benz_factory->create_car("A6"));

    unique_ptr<Light> l1(bmw_factory->create_car_light());
    unique_ptr<Light> l2(benz_factory->create_car_light());

    c1->show();
    c2->show();
    l1->show();
    l2->show();

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
简单工厂 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;}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
代理类

// 用代理类进行权限控制
// 免费电影代理类
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;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int main() {
    // 免费代理类,多态:基类指针指向派生类对象
    unique_ptr<VideoSite> p1(new FreeVideoSiteProxy());
    p1->free_video();
    p1->vip_video();
    p1->ticket_video();

    unique_ptr<VideoSite> p2(new VipVideoSiteProxy());
    p2->free_video();
    p2->vip_video();
    p2->ticket_video();

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
六、装饰器模式 Decorator
可用于现有类的功能扩充

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

使用装饰器进行功能扩充


车类

// 抽象基类
class Car {
public:
    virtual void show() = 0;
};

class BMW : public Car {
public:
    void show() {
        cout << "这是一辆宝马,有基本配置";
    }
};

class Benz : public Car {
public:
    void show() {
        cout << "这是一辆奔驰,有基本配置";
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
装饰器类

// 装饰器的基类
class CarDecorator : public Car {
public:
    // 对p进行装饰,Car*表示基类指针
    CarDecorator(Car* p) :p_car(p){}
    // 用基类指针保留装饰的对象
    Car* p_car;
};

// 功能类1
class FunDecorator1 : public CarDecorator {
public:
    FunDecorator1(Car* p): CarDecorator(p) {}
    void show() {
        p_car->show();
        // 添加新功能
        cout << ",自动刹车";
    }
};

// 功能类2
class FunDecorator2 : public CarDecorator {
public:
    FunDecorator2(Car* p) : CarDecorator(p) {}
    void show() {
        p_car->show();
        // 添加新功能
        cout << ",定速巡航";
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
装饰器使用

int main() {
    // 由于FunDecorator1和FunDecorator2的构造函数接收的都是基类的指针类型
    // 所以直接嵌套装饰
    
    unique_ptr<Car> bmw(new FunDecorator2(new FunDecorator1(new BMW())));
    bmw->show();
    cout << endl;

    unique_ptr<Car> benz(new FunDecorator2(new FunDecorator1(new Benz())));
    benz->show();
    cout << endl;
    return 0;

/*
这是一辆宝马,有基本配置,自动刹车,定速巡航
这是一辆奔驰,有基本配置,自动刹车,定速巡航
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
七、适配器模式
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接口进行视频播放
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
现在有一批新的投影仪,这些投影仪只支持HDMI接口

class HDMI {
public:
    virtual void play() = 0;
};

// 只支持VGA接口的投影仪
class Projector_HDMI : public HDMI {
public:
    void play() {
        cout << "投影仪通过HDMI接口进行视频播放" << endl;
    }
};
1
2
3
4
5
6
7
8
9
10
11
12
这时需要用只支持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;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main() {
    Computer().play_video(new VGAToHDMIAdapter(new Projector_HDMI()));
    return 0;
}
1
2
3
4


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

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

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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<Observer*> 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<int, list<Observer*>> _sub_map;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
测试代码

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;
}
————————————————
版权声明:本文为CSDN博主「只会写bug~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42500831/article/details/120120470

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值