学懂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

相关文章

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Python利用ElementTree实现快速解析XML文件

《Python利用ElementTree实现快速解析XML文件》ElementTree是Python标准库的一部分,而且是Python标准库中用于解析和操作XML数据的模块,下面小编就来和大家详细讲讲... 目录一、XML文件解析到底有多重要二、ElementTree快速入门1. 加载XML的两种方式2.

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

java解析jwt中的payload的用法

《java解析jwt中的payload的用法》:本文主要介绍java解析jwt中的payload的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解析jwt中的payload1. 使用 jjwt 库步骤 1:添加依赖步骤 2:解析 JWT2. 使用 N

Python中__init__方法使用的深度解析

《Python中__init__方法使用的深度解析》在Python的面向对象编程(OOP)体系中,__init__方法如同建造房屋时的奠基仪式——它定义了对象诞生时的初始状态,下面我们就来深入了解下_... 目录一、__init__的基因图谱二、初始化过程的魔法时刻继承链中的初始化顺序self参数的奥秘默认

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文