学懂C++(四十三):深入剖析现代C++设计模式:从常用经典到前沿的全面解析

本文主要是介绍学懂C++(四十三):深入剖析现代C++设计模式:从常用经典到前沿的全面解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

引言

1. 单例模式(Singleton)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

2. 工厂模式(Factory)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

3. 观察者模式(Observer)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

4. 策略模式(Strategy)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

5. 适配器模式(Adapter)

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

6. 现代设计模式

6.1 Type Erasure

概念

模型

特点

核心点

实现

适用场景

经典示例实现

代码解析

6.2 Curiously Recurring Template Pattern (CRTP)

概念

模型

特点

核心点

实现 

适用场景

经典示例实现

代码解析

特点

总结


 

引言

       设计模式是软件工程中解决特定问题的通用解决方案。它们并不是代码片段,而是对问题和解决方案的描述,旨在帮助开发者编写更易维护、更具扩展性的代码。本篇文章将深入讲解几种经典和现代设计模式,涵盖其概念、模型、特点、核心点、实现、适用场景,以及经典示例代码和详细解析。

1. 单例模式(Singleton)

概念

      单例模式确保一个类只有一个实例,并提供一个全局访问点。

模型

  • 私有构造函数:防止通过new关键字创建对象。
  • 静态私有实例指针:指向唯一的单例实例。
  • 公有静态方法:用于提供全局访问点,创建或返回实例。

特点

  • 唯一实例:全局唯一的实例,节省资源。
  • 懒汉式/饿汉式:实例的创建时机不同。

核心点

  • 确保类不能通过构造函数创建多实例。
  • 提供一个静态方法访问实例。

实现

class Singleton
{
private:static Singleton* instance;// 私有构造函数Singleton() {}public:// 禁用拷贝构造和赋值操作Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;static Singleton* getInstance(){if (instance == nullptr){instance = new Singleton();}return instance;}
};// 初始化静态成员
Singleton* Singleton::instance = nullptr;

适用场景

  • 需要控制系统或模块中的全局唯一实例,如日志记录器、配置管理器等。

经典示例实现

#include <iostream>class Logger
{
private:static Logger* instance;Logger() {}public:Logger(const Logger&) = delete;Logger& operator=(const Logger&) = delete;static Logger* getInstance(){if (instance == nullptr){instance = new Logger();}return instance;}void log(const std::string& message){std::cout << "Log: " << message << std::endl;}
};Logger* Logger::instance = nullptr;int main()
{Logger::getInstance()->log("Singleton Pattern Example");return 0;
}

代码解析

  1. 私有构造函数:防止通过new创建对象。
  2. 静态公有方法getInstance:提供全局访问点,懒汉式创建实例。
  3. log方法:示例业务方法。

2. 工厂模式(Factory)

概念

工厂模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。

模型

  • 工厂接口/抽象类:定义创建对象的方法。
  • 具体工厂类:实现创建对象的方法。

特点

  • 解耦:客户端代码与具体类解耦。
  • 扩展性:增加新产品时,只需添加新的具体工厂和产品类。

核心点

  • 提供一个创建对象的接口。
  • 将对象的创建延迟到子类。

实现

class Product
{
public:virtual void use() = 0;
};class ConcreteProductA : public Product
{
public:void use() override { std::cout << "Using ConcreteProductA" << std::endl; }
};class ConcreteProductB : public Product
{
public:void use() override { std::cout << "Using ConcreteProductB" << std::endl; }
};class Factory
{
public:virtual Product* createProduct() = 0;
};class ConcreteFactoryA : public Factory
{
public:Product* createProduct() override { return new ConcreteProductA(); }
};class ConcreteFactoryB : public Factory
{
public:Product* createProduct() override { return new ConcreteProductB(); }
};

适用场景

  • 客户端不需要知道具体产品类的创建逻辑。
  • 需要提供灵活的产品创建机制。

经典示例实现

#include <iostream>class Product
{
public:virtual void use() = 0;
};class ConcreteProductA : public Product
{
public:void use() override { std::cout << "Using ConcreteProductA" << std::endl; }
};class ConcreteProductB : public Product
{
public:void use() override { std::cout << "Using ConcreteProductB" << std::endl; }
};class Factory
{
public:virtual Product* createProduct() = 0;
};class ConcreteFactoryA : public Factory
{
public:Product* createProduct() override { return new ConcreteProductA(); }
};class ConcreteFactoryB : public Factory
{
public:Product* createProduct() override { return new ConcreteProductB(); }
};int main()
{Factory* factoryA = new ConcreteFactoryA();Product* productA = factoryA->createProduct();productA->use();Factory* factoryB = new ConcreteFactoryB();Product* productB = factoryB->createProduct();productB->use();delete productA;delete productB;delete factoryA;delete factoryB;return 0;
}

代码解析

  1. Product:定义产品的接口。
  2. ConcreteProductAConcreteProductB:实现具体产品。
  3. Factory:定义工厂接口。
  4. ConcreteFactoryAConcreteFactoryB:实现具体工厂。

3. 观察者模式(Observer)

概念

观察者模式定义对象间的一种一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新。

模型

  • 主题接口/抽象类:提供添加、删除和通知观察者的方法。
  • 具体主题类:实现主题接口,维护观察者列表。
  • 观察者接口/抽象类:定义更新方法。
  • 具体观察者类:实现观察者接口,执行具体更新逻辑。

特点

  • 解耦:观察者和主题之间的低耦合。
  • 灵活性:观察者可以在运行时动态添加或删除。

核心点

  • 维护一个观察者列表。
  • 通知所有观察者更新。

实现

#include <iostream>
#include <vector>
#include <algorithm>class Observer
{
public:virtual void update(int state) = 0;
};class Subject
{
private:std::vector<Observer*> observers;int state;public:void attach(Observer* observer){observers.push_back(observer);}void detach(Observer* observer){observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void notify(){for (Observer* observer : observers){observer->update(state);}}void setState(int state){this->state = state;notify();}int getState() const{return state;}
};class ConcreteObserver : public Observer
{
private:std::string name;Subject& subject;public:ConcreteObserver(std::string name, Subject& subject) : name(name), subject(subject) {}void update(int state) override{std::cout << "Observer " << name << " received update: " << state << std::endl;}
};

适用场景

  • 一个对象的改变需要触发其他对象的变化。
  • 不知道具体有多少对象需要被通知。

经典示例实现

#include <iostream>
#include <vector>
#include <algorithm>class Observer
{
public:virtual void update(int state) = 0;
};class Subject
{
private:std::vector<Observer*> observers;int state;public:void attach(Observer* observer){observers.push_back(observer);}void detach(Observer* observer){observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void notify(){for (Observer* observer : observers){observer->update(state);}}void setState(int state){this->state = state;notify();}int getState() const{return state;}
};class ConcreteObserver : public Observer
{
private:std::string name;Subject& subject;public:ConcreteObserver(std::string name, Subject& subject) : name(name), subject(subject) {}void update(int state) override{std::cout << "Observer " << name << " received update: " << state << std::endl;}
};int main()
{Subject subject;ConcreteObserver observer1("Observer1", subject);ConcreteObserver observer2("Observer2", subject);subject.attach(&observer1);subject.attach(&observer2);subject.setState(1);subject.setState(2);subject.detach(&observer1);subject.setState(3);return 0;
}

代码解析

  1. Observer:定义观察者接口。
  2. Subject:维护观察者列表,状态更新时通知观察者。
  3. ConcreteObserver:实现观察者接口,处理更新逻辑。

4. 策略模式(Strategy)

概念

策略模式定义一系列算法,把它们一个个封装起来,并且使它们可互相替换,使得算法可以独立于使用它的客户端而变化。

模型

  • 策略接口/抽象类:定义算法接口。
  • 具体策略类:实现具体算法。
  • 上下文类:持有策略对象,并通过策略接口调用具体算法。

特点

  • 算法的独立性:算法可以独立于上下文变化。
  • 灵活性:可以动态更改算法。

核心点

  • 定义策略接口。
  • 上下文持有策略对象,通过接口调用算法。

实现

class Strategy
{
public:virtual void algorithm() = 0;
};class ConcreteStrategyA : public Strategy
{
public:void algorithm() override { std::cout << "Algorithm A" << std::endl; }
};class ConcreteStrategyB : public Strategy
{
public:void algorithm() override { std::cout << "Algorithm B" << std::endl; }
};class Context
{
private:Strategy* strategy;public:Context(Strategy* strategy) : strategy(strategy) {}void setStrategy(Strategy* strategy){this->strategy = strategy;}void executeStrategy(){strategy->algorithm();}
};

适用场景

  • 需要使用不同的算法解决问题,并且可以在运行时切换算法。
  • 避免使用多重条件语句(if-else或switch-case)。

经典示例实现

#include <iostream>class Strategy
{
public:virtual void algorithm() = 0;
};class ConcreteStrategyA : public Strategy
{
public:void algorithm() override { std::cout << "Algorithm A" << std::endl; }
};class ConcreteStrategyB : public Strategy
{
public:void algorithm() override { std::cout << "Algorithm B" << std::endl; }
};class Context
{
private:Strategy* strategy;public:Context(Strategy* strategy) : strategy(strategy) {}void setStrategy(Strategy* strategy){this->strategy = strategy;}void executeStrategy(){strategy->algorithm();}
};int main()
{ConcreteStrategyA strategyA;ConcreteStrategyB strategyB;Context context(&strategyA);context.executeStrategy();context.setStrategy(&strategyB);context.executeStrategy();return 0;
}

代码解析

  1. Strategy:定义算法接口。
  2. ConcreteStrategyAConcreteStrategyB:实现具体算法。
  3. Context:持有策略对象,通过接口调用算法。

5. 适配器模式(Adapter)

概念

适配器模式将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

模型

  • 目标接口:定义客户所期待的接口。
  • 适配者类:定义现有接口。
  • 适配器类:通过组合或继承,将适配者接口转换为目标接口。

特点

  • 重新利用现有代码:通过适配器,使得现有接口与新接口兼容。
  • 解耦:客户端代码与适配者类解耦。

核心点

  • 定义目标接口。
  • 适配器类实现目标接口,通过组合或继承适配者类。

实现

class Target
{
public:virtual void request() = 0;
};class Adaptee
{
public:void specificRequest() { std::cout << "Specific Request" << std::endl; }
};class Adapter : public Target
{
private:Adaptee* adaptee;public:Adapter(Adaptee* adaptee) : adaptee(adaptee) {}void request() override{adaptee->specificRequest();}
};

适用场景

  • 需要将现有类的接口转换为另一接口。
  • 使得原本不兼容的类一起工作。

经典示例实现

#include <iostream>class Target
{
public:virtual void request() = 0;
};class Adaptee
{
public:void specificRequest() { std::cout << "Specific Request" << std::endl; }
};class Adapter : public Target
{
private:Adaptee* adaptee;public:Adapter(Adaptee* adaptee) : adaptee(adaptee) {}void request() override{adaptee->specificRequest();}
};int main()
{Adaptee adaptee;Adapter adapter(&adaptee);adapter.request();return 0;
}

代码解析

  1. Target:定义客户所期待的接口。
  2. Adaptee:定义现有接口,需要被适配。
  3. Adapter:实现目标接口,通过组合适配者类,将其接口转换为目标接口。

6. 现代设计模式

6.1 Type Erasure

概念

Type Erasure是一种技术,使得在运行时仍能使用不同类型的对象,而不需要在编译时知道具体类型。

模型

  • 接口类:定义统一接口。
  • 具体实现类:使用模板类实现接口。
  • 持有器类:持有具体实现对象,通过接口类访问其功能。

特点

  • 灵活性:可以处理不同类型的对象,而不需要在编译时知道具体类型。
  • 扩展性:可以添加新类型的对象,而不需要修改现有代码。

核心点

  • 定义统一接口。
  • 使用模板类实现接口,并持有具体实现对象。

实现

class AnyFunction
{
public:virtual ~AnyFunction() = default;virtual void invoke() const = 0;
};template <typename Func>
class ConcreteFunction : public AnyFunction
{
private:Func func;public:ConcreteFunction(Func func) : func(func) {}void invoke() const override { func(); }
};class TypeErasedFunction
{
private:AnyFunction* func;public:template <typename Func>TypeErasedFunction(Func func) : func(new ConcreteFunction<Func>(func)) {}~TypeErasedFunction(){delete func;}void operator()() const{func->invoke();}
};

适用场景

  • 需要处理不同类型的对象,而不需要在编译时知道具体类型。
  • 需要灵活扩展新类型的对象。

经典示例实现

#include <iostream>class AnyFunction
{
public:virtual ~AnyFunction() = default;virtual void invoke() const = 0;
};template <typename Func>
class ConcreteFunction : public AnyFunction
{
private:Func func;public:ConcreteFunction(Func func) : func(func) {}void invoke() const override { func(); }
};class TypeErasedFunction
{
private:AnyFunction* func;public:template <typename Func>TypeErasedFunction(Func func) : func(new ConcreteFunction<Func>(func)) {}~TypeErasedFunction(){delete func;}void operator()() const{func->invoke();}
};void exampleFunction()
{std::cout << "Example Function" << std::endl;
}int main()
{TypeErasedFunction func(exampleFunction);func();return 0;
}

代码解析

  1. AnyFunction:定义统一接口。
  2. ConcreteFunction:使用模板类实现接口,持有具体实现对象。
  3. TypeErasedFunction:持有具体实现对象,通过接口类访问其功能。

6.2 Curiously Recurring Template Pattern (CRTP)

概念

CRTP是一种C++编程技巧,其中一个类将自己作为模板参数传递给基类。

模型

  • 基类模板:使用派生类作为模板参数。
  • 派生类:继承基类模板,并将自己作为模板参数传递给基类模板。

特点

  • 静态多态:在编译时决定类型和调用方法,而不是运行时。
  • 无需虚函数开销:避免了虚函数的运行时开销。

核心点

  • 基类模板使用派生类作为模板参数。
  • 派生类继承基类模板,并将自己作为模板参数传递给基类模板。

实现 

template <typename Derived>
class Base
{
public:void interface(){static_cast<Derived*>(this)->implementation();}void implementation(){std::cout << "Base implementation" << std::endl;}
};class Derived : public Base<Derived>
{
public:void implementation(){std::cout << "Derived implementation" << std::endl;}
};

适用场景

  • 需要静态多态的场景,如静态接口仿真。
  • 希望避免虚函数开销的场景。

经典示例实现

#include <iostream>template <typename Derived>
class Base
{
public:void interface(){static_cast<Derived*>(this)->implementation();}void implementation(){std::cout << "Base implementation" << std::endl;}
};class Derived : public Base<Derived>
{
public:void implementation(){std::cout << "Derived implementation" << std::endl;}
};int main()
{Derived d;d.interface();return 0;
}

代码解析

  1. Base模板类:定义接口方法interface,通过静态转换调用派生类的实现。
  2. Derived:继承基类模板,并实现具体方法implementation
  3. main函数:创建Derived对象并调用接口方法interface,展示静态多态。

特点

  1. 静态多态:在编译时确定调用的具体方法。
  2. 避免虚函数开销:CRTP通过模板实现多态,避免了虚函数的运行时开销。

总结

       本文详细介绍了几种经典和现代设计模式在C++中的应用。每种设计模式都包含其概念、模型、特点、核心点、实现方法、适用场景,以及经典示例代码和详细解析。

  • 单例模式:确保一个类只有一个实例,并提供一个全局访问点。适用于需要唯一实例的场景,如日志记录器。
  • 工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。适用于客户端不需要知道具体产品类的创建逻辑的场景。
  • 观察者模式:定义对象间的一种一对多依赖关系,当一个对象的状态改变时,所有依赖它的对象都会收到通知并自动更新。适用于一个对象的改变需要触发其他对象的变化的场景。
  • 策略模式:定义一系列算法,把它们一个个封装起来,使它们可以互相替换。适用于需要使用不同的算法解决问题的场景。
  • 适配器模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适用于需要将现有类的接口转换为另一接口的场景。
  • Type Erasure:通过运行时多态和模板实现类型擦除,使不同类型的对象可以通过统一接口访问。适用于需要处理不同类型的对象而不需要在编译时知道具体类型的场景。
  • CRTP:通过模板实现静态多态,避免虚函数开销。适用于需要静态多态的场景,并希望避免虚函数开销。

        这些设计模式不仅能帮助开发者编写更易维护、更具扩展性的代码,还能提升代码的灵活性和复用性。通过结合这些设计模式,开发者可以更好地应对软件开发中的复杂问题,提高软件质量和开发效率。

 

 

这篇关于学懂C++(四十三):深入剖析现代C++设计模式:从常用经典到前沿的全面解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105381

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/