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

相关文章

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

Linux上设置Ollama服务配置(常用环境变量)

《Linux上设置Ollama服务配置(常用环境变量)》本文主要介绍了Linux上设置Ollama服务配置(常用环境变量),Ollama提供了多种环境变量供配置,如调试模式、模型目录等,下面就来介绍一... 目录在 linux 上设置环境变量配置 OllamPOgxSRJfa手动安装安装特定版本查看日志在

Java常用注解扩展对比举例详解

《Java常用注解扩展对比举例详解》:本文主要介绍Java常用注解扩展对比的相关资料,提供了丰富的代码示例,并总结了最佳实践建议,帮助开发者更好地理解和应用这些注解,需要的朋友可以参考下... 目录一、@Controller 与 @RestController 对比二、使用 @Data 与 不使用 @Dat

Mysql中深分页的五种常用方法整理

《Mysql中深分页的五种常用方法整理》在数据量非常大的情况下,深分页查询则变得很常见,这篇文章为大家整理了5个常用的方法,文中的示例代码讲解详细,大家可以根据自己的需求进行选择... 目录方案一:延迟关联 (Deferred Join)方案二:有序唯一键分页 (Cursor-based Paginatio

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

Python实现常用文本内容提取

《Python实现常用文本内容提取》在日常工作和学习中,我们经常需要从PDF、Word文档中提取文本,本文将介绍如何使用Python编写一个文本内容提取工具,有需要的小伙伴可以参考下... 目录一、引言二、文本内容提取的原理三、文本内容提取的设计四、文本内容提取的实现五、完整代码示例一、引言在日常工作和学