c++设计模式详解_结构型设计模式

简介: c++设计模式详解_结构型设计模式

结构型设计模式:

一、单例模式:

定义:保证一个类只有一个实例,并提供一个该实例的全局访问接口。
代码举例:
  1. 版本一
    缺点:线程不安全
class Singleton
{
public:
    static Singleton *GetInstance()
    {
        if (_instance == nullptr){
            _instance = new Singleton();
            atexit(Destructor);  // atexit 是线程安全的
        }
        return _instance;
    }
private:
    static void Destructor()
    {
        if (nullptr != _instance){ 
            delete _instance;
            _instance = nullptr;
        }
    }
    Singleton(){}; //构造
    ~Singleton(){};
    Singleton(const Singleton &) = delete; //拷⻉构造
    Singleton &operator=(const Singleton &) = delete;  //拷贝赋值构造
    Singleton(Singleton &&) = delete; //移动构造
    Singleton &operator=(Singleton &&) = delete; //移动拷贝构造
    static Singleton *_instance;
};
Singleton *Singleton::_instance = nullptr; //静态成员需要初始化
  1. 版本二
    说明:线程安全 懒汉模式
#include <mutex>
Singleton *Singleton::_instance = nullptr; //静态成员需要初始化
std::mutex Singleton::_mutex;              //互斥锁初始化
class Singleton
{ 
public:
    static Singleton *GetInstance()
    {
        // std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程
        if (_instance == nullptr)
        {
            std::lock_guard<std::mutex> lock(_mutex); // 3.2
            if (_instance == nullptr)
            {
                _instance = new Singleton();
                // 1. 分配内存 2. 调用构造函数 3. 返回指针  
                // 问题:多线程环境下,会出现CPU指令重排,比如:132  这样如果在没调用构造函数,就返回了指针,操作该指针会报错
                atexit(Destructor);
            }
        }
        return _instance;
    }
private:
    static void Destructor()
    {
        if (nullptr != _instance){
            delete _instance;
            _instance = nullptr;
        }
    }
    Singleton(){}; //构造
    ~Singleton(){};
    Singleton(const Singleton &) = delete; //拷⻉构造
    Singleton &operator=(const Singleton &) =  delete;  //拷贝赋值构造
    Singleton(Singleton &&) = delete; //移动构造
    Singleton &operator=(Singleton &&) = delete; //移动拷贝构造
    static Singleton *_instance;
    static std::mutex _mutex;
};
  1. 版本三
    说明:解决多线程情况下,cpu指令重排问题。
同步原语

分为:原子变量内存栅栏。其中,原子变量负责解决原子行和可见性,内存栅栏负责解决执行序问题。

load 就是可以看见其他线程最新操作的数据

store 就是让别的线程可以看到自己线程的修改

static std::atomic<Singleton *> _instance;  //原子变量需要添加头文件 #include <mutex>
Singleton *tmp = _instance.load(std::memory_order_relaxed); // 
_instance.store(tmp, std::memory_order_relaxed); //
内存模型

memory_order_acquirememory_order_release之间的代码,不会被优化到他们之外的部分。

memory_order_relaxed 优点:内存模型使用relaxed进行优化,速度快

#include <mutex>
#include <atomic>
class Singleton
{
public:
    static Singleton *GetInstance()
    {
        Singleton *tmp = _instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire); //获取内存屏障,也就是开启内存屏障
        if (tmp == nullptr)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            tmp =_instance.load(std::memory_order_relaxed);  // 内存模型使用relaxed进行优化,速度快
            if (tmp == nullptr){
                tmp = new Singleton;  // 在内存屏障的区域内,乱序没有关系,不会跑出内存屏障的范围
                std::atomic_thread_fence(std::memory_order_release); //释放内存屏障
                _instance.store(tmp, std::memory_order_relaxed);
                atexit(Destructor);
            }
        }
        return tmp;
    }
private:
    static void Destructor(){
        Singleton *tmp = _instance.load(std::memory_order_relaxed);
        if (nullptr != tmp){
            delete tmp;
        }
    }
    Singleton(){}; //构造
    ~Singleton(){};
    Singleton(const Singleton &) = delete; //拷⻉构造
    Singleton &operator=(const Singleton &) = delete; /拷贝赋值构造
    Singleton(Singleton &&) = delete; //移动构造
    Singleton &operator=(Singleton &&) = delete; //移动拷贝构造
    static std::atomic<Singleton *> _instance;
    static std::mutex _mutex;
};
std::atomic<Singleton *> Singleton::_instance; //静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
  1. 版本四 (最简单的写法)
    返回类的局部静态变量的引用。c++11静态局部变量初始化,线程安全
class Singleton
{
public:
    static Singleton &GetInstance(){
        static Singleton instance;
        return instance;
    }
private:
    Singleton(){}; //构造
    ~Singleton(){};
    Singleton(const Singleton &) = delete; //拷⻉构造
    Singleton &operator=(const Singleton &) = delete; //拷贝赋值构造
    Singleton(Singleton &&) = delete; //移动构造
    Singleton &operator=(Singleton &&) = delete; //移动拷贝构造
};
  1. 版本五
    具有版本四的所有优点:
    a)利用局部静态变量特性,延迟加载;
    b)利用局部静态变量特性,系统自动挥手内存,自动调用析构函数;
    c)静态局部变量初始化时,没有new操作带来的cpu指令重排问题;
    d)c++11静态局部变量初始化,线程安全

为了让基类的Singleton访问到子类DesignPattern的私有构造函数,所以,需要在子类中声明基类为友元类。友元函数可以访问该类的私有成员和属性。

template <typename T>
class Singleton
{
public:
    static T &GetInstance(){
        static T instance; // 这⾥要初始化DesignPattern,需要调⽤DesignPattern 构造函数,同时会调⽤⽗类的构造函数。 
        return instance;
    }
protected:
    virtual ~Singleton() {}
    Singleton() {} // protected修饰构造函数,才能让别⼈继承
private:
    Singleton(const Singleton &) = delete; //拷⻉构造
    Singleton &operator=(const Singleton &) = delete;  //拷贝赋值构造
    Singleton(Singleton &&) = delete; //移动构造
    Singleton &operator=(Singleton &&) = delete; //移动拷贝构造
};
// 子类DesignPattern 继承父类 Singleton<DesignPattern>
class DesignPattern : public Singleton<DesignPattern>
{
    friend class Singleton<DesignPattern>; //friend 能让Singleton<T> 访问到 DesignPattern构造函数 
private :           
    DesignPattern() {}
    ~DesignPattern() {}
};

二、工厂方法

实现代码举例:

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json 后面可能扩展excel格式csv
class IExport {
public:
    virtual bool Export(const std::string &data) = 0;  // 纯虚函数,多态
    virtual ~IExport(){}  // 虚析构函数
};
class ExportXml : public IExport {
public:
  // 需要实现父类的纯虚函数
    virtual bool Export(const std::string &data) {
        return true;
    }
};
class ExportJson : public IExport {
public:
  // 需要实现父类的纯虚函数
    virtual bool Export(const std::string &data) {
        return true;
    }
};
// 扩展csv格式
/*
class ExportCSV : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};
*/
// 工厂对外提供导出接口
class IExportFactory {
public:
    IExportFactory() {
        _export = nullptr;
    }
    virtual ~IExportFactory() {
        if (_export) {
            delete _export;
            _export = nullptr;
        }
    }
    bool Export(const std::string &data) {
        if (_export == nullptr) {
            _export = NewExport();
        }
        return _export->Export(data);
    }
protected:
    virtual IExport * NewExport(/* ... */) = 0;
private:
    IExport* _export;  //基类指针
};
class ExportXmlFactory : public IExportFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportXml();
        // 可能之后有什么操作
        return temp;
    }
};
class ExportJsonFactory : public IExportFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportJson;
        // 可能之后有什么操作
        return temp;
    }
};
// 扩展csv格式
/* 
class ExportCSVFactory : public IExportFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportCSV;
        // 可能之后有什么操作
        return temp;
    }
};
*/
int main () {
    IExportFactory *factory = new ExportCSVFactory();
    factory->Export("hello world");
    return 0;
}

三、抽象工厂方法

代码实现举例

#include <string>
// 实现导出数据的接口, 导出数据的格式包含 xml,json,文本格式txt 后面可能扩展excel格式csv
class IExport {
public:
    virtual bool Export(const std::string &data) = 0;
    virtual ~IExport(){}
};
class ExportXml : public IExport {
public:
    virtual bool Export(const std::string &data) {
        return true;
    }
};
class IImport {
public:
    virtual bool Import(const std::string &data) = 0;
    virtual ~IImport(){}
};
class ImportXml : public IImport {
public:
    virtual bool Import(const std::string &data) {
        return true;
    }
};
class IDataApiFactory {
public:
    IDataApiFactory() {
        _export = nullptr;
        _import = nullptr;
    }
    virtual ~IDataApiFactory() {
        if (_export) {
            delete _export;
            _export = nullptr;
        }
        if (_import) {
            delete _import;
            _import = nullptr;
        }
    }
    bool Export(const std::string &data) {
        if (_export == nullptr) {
            _export = NewExport();
        }
        return _export->Export(data);
    }
    bool Import(const std::string &data) {
        if (_import == nullptr) {
            _import = NewImport();
        }
        return _import->Import(data);
    }
protected:
    virtual IExport * NewExport(/* ... */) = 0;
    virtual IImport * NewImport(/* ... */) = 0;
private:
    IExport *_export;
    IImport *_import;
};
class XmlApiFactory : public IDataApiFactory {
protected:
    virtual IExport * NewExport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IExport * temp = new ExportXml;
        // 可能之后有什么操作
        return temp;
    }
    virtual IImport * NewImport(/* ... */) {
        // 可能有其它操作,或者许多参数
        IImport * temp = new ImportXml;
        // 可能之后有什么操作
        return temp;
    }
};
// 相关性  依赖性    工作当中
int main () {
    IDataApiFactory *factory = new CSVApiFactory();
    factory->Import("hello world");
    factory->Export("hello world");
    return 0;
}

四、责任链

接收者形成一个链条,发送者发送的请求,从第一个开始,往后顺延,直到被处理为止,处理完后面就不会再处理了。简单理解:一个一个传递,有打断功能。

场景:请假流程,1天内需要直系领导审批;3天内需要经理审批,3天以上需要董事长审批。

核心点:我能处理我处理,处理不了往下传。

代码实现举例:

#include <string>
class Context {
public:
    std::string name;
    int day;
};
// 稳定点 抽象  变化点 扩展 (多态)
//  从单个处理节点出发,我能处理,我处理,我不能处理交给下一个人处理
//  链表关系如何抽象
class IHandler {
public:
    virtual ~IHandler() : next(nullptr) {}
    void SetNextHandler(IHandler *next) { // 链表关系
        next = next;
    }
    bool Handle(const Context &ctx) {
        if (CanHandle(ctx)) {
            return HandleRequest(ctx);
        } else if (GetNextHandler()) {
            return GetNextHandler()->Handle(ctx);
        } else {
            // err
        }
        return false;
    }
    // 通过函数来抽象 处理节点的个数  处理节点顺序
    static bool handler_leavereq(Context &ctx) {
        IHandler * h0 = new HandleByBeauty();
        IHandler * h1 = new HandleByMainProgram();
        IHandler * h2 = new HandleByProjMgr();
        IHandler * h3 = new HandleByBoss();
        h0->SetNextHandler(h1);
        h1->SetNextHandler(h2);
        h2->SetNextHandler(h3);
        return h0->Handle(ctx);
    }
protected:
    virtual bool HandleRequest(const Context &ctx) {return true};
    virtual bool CanHandle(const Context &ctx) {return true};
    IHandler * GetNextHandler() {
        return next;
    }
private:
    IHandler *next; // 组合基类指针
};
// 能不能处理,以及怎么处理
class HandleByMainLeader : public IHandler {
protected:
    virtual bool HandleRequest(const Context &ctx){
        //
        return true;
    }
    virtual bool CanHandle(const Context &ctx) {
        //
        if (ctx.day <= 10)
            return true;
        return false;
    }
};
class HandleByMgr : public IHandler {
protected:
    virtual bool HandleRequest(const Context &ctx){
        //
        return true;
    }
    virtual bool CanHandle(const Context &ctx) {
        //
        if (ctx.day <= 20)
            return true;
        return false;
    }
};
class HandleByBoss : public IHandler {
protected:
    virtual bool HandleRequest(const Context &ctx){
        //
        return true;
    }
    virtual bool CanHandle(const Context &ctx) {
        //
        if (ctx.day < 30)
            return true;
        return false;
    }
};
class HandleByBeauty : public IHandler {
protected:
    virtual bool HandleRequest(const Context &ctx){
        //
        return true;
    }
    virtual bool CanHandle(const Context &ctx) {
        //
        if (ctx.day <= 3)
            return true;
        return false;
    }
};
int main() {
    // nginx http 处理 就是责任连模式
    // 设置下一指针 
    Context ctx;
    if (IHander::handler_leavereq(ctx)) {
        cout << "请假成功";
    } else {
        cout << "请假失败";
    }
    return 0;
}

五、装饰器模式

定义

动态地给一个对象增加一些额外的职责。比生产子类更加灵活。

什么时候使用

不影响其他对象的情况下,以动态、透明的方式给对象添加职责;每个职责都是完全独立的功能,彼此之间没有依赖

代码实现举例
#include <iostream>
// 普通员工有销售奖金,累计奖金,部门经理除此之外还有团队奖金;后面可能会添加环比增长奖金,同时可能产生不同的奖金组合;
// 销售奖金 = 当月销售额 * 4%
// 累计奖金 = 总的回款额 * 0.2%
// 部门奖金 = 团队销售额 * 1%
// 环比奖金 = (当月销售额-上月销售额) * 1%
// 销售后面的参数可能会调整
using namespace std;
class Context {
public:
    bool isMgr;
    // User user;
    // double groupsale;
};
class CalcBonus {    
public:
    CalcBonus(CalcBonus * c = nullptr) : cc(c) {}
    virtual double Calc(Context &ctx) {
        return 0.0; // 基本工资
    }
    virtual ~CalcBonus() {}
protected:
    CalcBonus* cc;
};
class CalcMonthBonus : public CalcBonus {
public:
    CalcMonthBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double mbonus /*= 计算流程忽略*/; 
        return mbonus + cc->Calc(ctx);
    }
};
class CalcSumBonus : public CalcBonus {
public:
    CalcSumBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double sbonus /*= 计算流程忽略*/; 
        return sbonus + cc->Calc(ctx);
    }
};
class CalcGroupBonus : public CalcBonus {
public:
    CalcGroupBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double gbnonus /*= 计算流程忽略*/; 
        return gbnonus + cc->Calc(ctx);
    }
};
class CalcCycleBonus : public CalcBonus {
public:
    CalcCycleBonus(CalcBonus * c) : CalcBonus(c) {}
    virtual double Calc(Context &ctx) {
        double gbnonus /*= 计算流程忽略*/; 
        return gbnonus + cc->Calc(ctx);
    }
};
int main() {
    // 1. 普通员工
    Context ctx1;
    CalcBonus *base = new CalcBonus();
    CalcBonus *cb1 = new CalcMonthBonus(base);
    CalcBonus *cb2 = new CalcSumBonus(cb1);
    cb2->Calc(ctx1);
    // 2. 部门经理
    Context ctx2;
    CalcBonus *cb3 = new CalcGroupBonus(cb1);
    cb3->Calc(ctx2);
}

组合模式

定义

将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

什么时候使用
  1. 如果你想表示对象的部分-整体层次结构,可以选用组合模式,把整体和部分的操作统一起来。
  2. 如果你希望统一地使用组合结构中的所有对象,可以选用组合模式,这正是组合模式提供的主要功能;
代码实现举例
class IComponent
{
public:
    IComponent(/* args */);
    ~IComponent();
    virtual void Execute() = 0;
    virtual void AddChild(IComponent *ele) {}
    virtual void RemoveChild(IComponent *ele) {}
};
class Leaf : public IComponent
{
public:
    virtual void Execute() {
        cout << "leaf exxcute" << endl;
    }
};
class Composite : public IComponent
{
private:
    std::list<IComponent*> _list;
public:
    virtual void AddChild(IComponent *ele) {
        // ...
    }
    virtual void RemoveChild(IComponent *ele) {
        // ...
    }
    virtual void Execute() {
        for (auto iter = _list.begin(); iter != _list.end(); iter++) {
            iter->Execute();
        }
    }
};

文章参考与<零声教育>的C/C++linux服务期高级架构

相关文章
|
5月前
|
设计模式 C++
C++一分钟之-设计模式:工厂模式与抽象工厂
【7月更文挑战第14天】设计模式是解决软件设计问题的通用方案。工厂模式与抽象工厂模式是创建型模式,用于对象创建而不暴露创建逻辑。工厂模式推迟实例化到子类,但过度使用会增加复杂性。抽象工厂则创建相关对象族,但过度抽象可能造成不必要的复杂度。两者均应按需使用,确保设计灵活性。代码示例展示了C++中如何实现这两种模式。
49 3
|
5月前
|
设计模式 安全 C++
C++一分钟之-C++中的设计模式:单例模式
【7月更文挑战第13天】单例模式确保类只有一个实例,提供全局访问。C++中的实现涉及线程安全和生命周期管理。基础实现使用静态成员,但在多线程环境下可能导致多个实例。为解决此问题,采用双重检查锁定和`std::mutex`保证安全。使用`std::unique_ptr`管理生命周期,防止析构异常和内存泄漏。理解和正确应用单例模式能提升软件的效率与可维护性。
70 2
|
7月前
|
设计模式 开发框架 算法
C++中的设计模式:基本概念与应用
C++中的设计模式:基本概念与应用
75 2
|
6月前
|
设计模式 程序员
结构型设计模式之适配器模式
结构型设计模式之适配器模式
|
6月前
|
设计模式
结构型设计模式之装饰模式
结构型设计模式之装饰模式
|
6月前
|
设计模式 编解码 网络安全
结构型设计模式之代理模式
结构型设计模式之代理模式
|
7月前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~结构型]不兼容结构的协调——适配器模式
[设计模式Java实现附plantuml源码~结构型]不兼容结构的协调——适配器模式
|
7月前
|
设计模式 安全 Java
[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式
[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式
|
7月前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~结构型] 扩展系统功能——装饰模式
[设计模式Java实现附plantuml源码~结构型] 扩展系统功能——装饰模式
|
7月前
|
设计模式 JavaScript 前端开发
[设计模式Java实现附plantuml源码~结构型] 提供统一入口——外观模式
[设计模式Java实现附plantuml源码~结构型] 提供统一入口——外观模式