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

相关文章

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

MySQL常用字符串函数示例和场景介绍

《MySQL常用字符串函数示例和场景介绍》MySQL提供了丰富的字符串函数帮助我们高效地对字符串进行处理、转换和分析,本文我将全面且深入地介绍MySQL常用的字符串函数,并结合具体示例和场景,帮你熟练... 目录一、字符串函数概述1.1 字符串函数的作用1.2 字符串函数分类二、字符串长度与统计函数2.1

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

全面解析Golang 中的 Gorilla CORS 中间件正确用法

《全面解析Golang中的GorillaCORS中间件正确用法》Golang中使用gorilla/mux路由器配合rs/cors中间件库可以优雅地解决这个问题,然而,很多人刚开始使用时会遇到配... 目录如何让 golang 中的 Gorilla CORS 中间件正确工作一、基础依赖二、错误用法(很多人一开

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方