优化C++设计模式:用模板代替虚函数与多态机制

ops/2024/11/18 18:46:17/

文章目录

    • 0. 引言
    • 1. 模板编程替换虚函数多态的必要性
    • 2. 设计模式改进
      • 2.1. 单例模式的改进
        • 与静态局部变量的对比(第二种实现)
      • 2.2. 工厂模式的改进
      • 2.3. 观察者模式的改进
      • 2.4. 适配器模式的改进
      • 2.5. 桥接模式的改进
      • 2.6. 装饰器模式的改进
      • 2.7. 责任链模式的改进

0. 引言

MISRA C++ 标准对类型转换和虚函数使用做出严格规定,尤其在嵌入式系统中,以确保代码的可预测性和高效性。传统设计模式虚函数多态性引入会增加运行时开销,增加系统负担。然而,模板编程通过编译时决策代替运行时动态绑定,可以提升性能并降低内存消耗。
本文将尝试使用C++11改进常用的几个设计模式

1. 模板编程替换虚函数多态的必要性

1.1. MISRA C++对类型转换和虚函数的规定

MISRA C++ 标准对于类型转换和虚函数的有严格的使用要求,要求包括:

  • Rule 5-0-1(MISRA C++ 2008):禁止使用不安全的类型转换,如 reinterpret_castconst_caststatic_castdynamic_cast
  • Rule 5-0-2:要求类型转换必须明确指定转换类型,确保转换符合预期行为,避免使用不安全的转换方式。
  • Rule 5-1-1:在进行类型转换时,必须确保转换类型合法,不破坏对象的常量性或类型。

同时,虚函数多态机制在嵌入式系统中也有劣势:

  • 运行时开销虚函数表(vtable)的查找会带来额外的运行时开销,增加程序的资源需求。
  • RTTI(运行时类型信息)开销:启用 RTTI 会增加内存使用,尤其是在 dynamic_casttypeid 被使用时。
  • MISRA C++的限制:标准要求避免使用虚函数多态机制,特别是在没有严格控制的情况下,可能会导致代码行为不可预测,增加出错的风险。

1.2. 模板编程的优势:替代虚函数多态机制

模板编程通过在编译时绑定函数和类,避免运行时的虚函数查找和多态性问题。使用模板编程的优势如下:

  • 编译时绑定模板代码在编译时就确定了函数调用和对象行为,避免了虚函数表的查找。
  • 内联优化:编译器能够对模板代码进行内联优化,直接将模板代码嵌入到调用点。
  • 提高执行效率:由于没有动态绑定和运行时类型信息,模板编程能够显著降低程序的运行时开销,提升性能。

模板编程避免了虚函数多态机制的开销,并且在遵循 MISRA C++ 标准时,能够更容易地实现高效且符合规范的设计模式

2. 设计模式改进

模板编程静态多态 通过在编译时进行决策,避免了虚函数调用和动态绑定的需要。以下是一些常见设计模式的改进方式,尽量在不违背设计原则的情况下提升代码的性能。

2.1. 单例模式的改进

传统的单例模式通过 static 变量和线程同步机制来保证只创建一个实例。然而,如果使用虚函数或者多态,在某些情况下可能会影响线程安全性,增加程序的复杂度。在 C++11/14 中,可以利用 std::once_flagstd::call_once 来实现线程安全的单例,而无需依赖虚函数

通过模板std::unique_ptr 来管理单例实例,可以避免虚函数的使用,同时保证线程安全和内存管理。

#include <iostream>
#include <memory>  // for std::unique_ptr
#include <mutex>class Singleton {public:// 获取唯一实例static Singleton& getInstance() {std::call_once(initFlag_, &Singleton::initSingleton);return *instance_;}// 示例方法void doSomething() {std::cout << "Singleton instance is doing something!" << std::endl;}private:Singleton() {std::cout << "Singleton initialized!" << std::endl;}// 禁止拷贝构造和赋值操作Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;// 使用 std::unique_ptr 管理单例实例static std::unique_ptr<Singleton> instance_;static std::once_flag initFlag_;// 初始化实例的私有方法static void initSingleton() {instance_ = std::make_unique<Singleton>();}
};// 初始化静态成员
std::unique_ptr<Singleton> Singleton::instance_ = nullptr;
std::once_flag Singleton::initFlag_;int main() {// 获取并使用单例对象Singleton& singleton1 = Singleton::getInstance();singleton1.doSomething();// 再次获取单例对象,确保不会重新初始化Singleton& singleton2 = Singleton::getInstance();singleton2.doSomething();return 0;
}

执行结果

Singleton initialized!
Singleton instance is doing something!
Singleton instance is doing something!
与静态局部变量的对比(第二种实现)

请看如下的实现实现:

class Singleton {public:static Singleton& getInstance() {static Singleton instance;return instance;}private:Singleton() = default;
};

区别对比

特性第一种实现第二种实现
线程安全性手动使用 std::once_flag 实现静态局部变量自动线程安全
复杂性较高,需维护多个静态成员和初始化方法较低,逻辑直接嵌入函数内
灵活性更灵活,适合复杂初始化逻辑不够灵活,构造函数绑定初始化逻辑
析构时机使用 std::unique_ptr,支持析构依赖静态变量销毁,时机不可控
适用场景更适合需要复杂初始化逻辑的场景适合大多数简单单例模式需求

2.2. 工厂模式的改进

工厂模式通常通过继承和虚函数来实例化不同的产品对象。而在使用模板的工厂模式中,我们通过将产品类型作为模板参数来实现静态多态,从而避免了使用虚函数。这种方式通过编译时的类型推导,确保了工厂方法能够在编译时确定,而不依赖于运行时的动态绑定。

CRTP(Curiously Recurring Template Pattern)是一种常见的技巧,它通过将子类作为模板参数,允许在基类中静态地定义行为,从而避免了虚函数的开销。通过这样的改进,我们可以在不牺牲灵活性的前提下,减少运行时的性能损耗。

#include <iostream>
#include <memory>
#include <type_traits>// 基础产品接口
template <typename ProductType>
class Product {public:void operation() const {static_assert(std::is_base_of<Product, ProductType>::value, "ProductType must be derived from Product");static_cast<const ProductType*>(this)->operationImpl();}private:// 使用 CRTP (Curiously Recurring Template Pattern) 进行静态多态void operationImpl() const = delete;
};// 具体产品A
class ConcreteProductA : public Product<ConcreteProductA> {public:void operationImpl() const {std::cout << "ConcreteProductA operation." << std::endl;}
};// 具体产品B
class ConcreteProductB : public Product<ConcreteProductB> {public:void operationImpl() const {std::cout << "ConcreteProductB operation." << std::endl;}
};// 工厂类模板
template <typename ProductType>
class Factory {public:std::unique_ptr<Product<ProductType>> create() const {return std::make_unique<ProductType>();}
};int main() {Factory<ConcreteProductA> factoryA;auto productA = factoryA.create();productA->operation();Factory<ConcreteProductB> factoryB;auto productB = factoryB.create();productB->operation();return 0;
}

执行结果

ConcreteProductA operation.
ConcreteProductB operation.

2.3. 观察者模式的改进

在传统的观察者模式中,观察者通常通过继承和虚函数来注册和通知其他对象。而在模板化的观察者模式中,我们使用 std::function 来替代虚函数,利用模板和类型擦除来实现更加高效和灵活的观察者模式。这样,我们避免了虚函数的调用和继承结构的复杂性,同时提高了类型安全性和灵活性。

#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <vector>template <typename EventHandler>
class Events {public:Events() : nextKey_(0) {}// 注册事件处理函数uint32_t addObserver(EventHandler&& handler) {uint32_t key = nextKey_++;storeHandler(key, std::forward<EventHandler>(handler));return key;}uint32_t addObserver(EventHandler& handler) {uint32_t key = nextKey_++;storeHandler(key, handler);return key;}// 移除事件处理函数void removeObserver(uint32_t key) {handlers_.erase(key);}// 通知所有注册的事件处理函数template <typename... Args>void notifyAll(Args&&... args) {for (auto& it : handlers_) {for (auto& handler : it.second) {handler(std::forward<Args>(args)...);}}}// 触发事件,通知所有注册的事件处理器template <typename... Args>inline void trigger(Args&&... args) {notifyAll(std::forward<Args>(args)...);}// 删除拷贝构造和赋值操作Events(const Events&) = delete;Events& operator=(const Events&) = delete;~Events() = default;// 通过 += 操作符添加事件处理器uint32_t operator+=(EventHandler&& handler) {return addObserver(std::forward<EventHandler>(handler));}uint32_t operator+=(EventHandler& handler) {return addObserver(handler);}// 通过 -= 操作符移除事件处理器Events& operator-=(uint32_t key) {removeObserver(key);return *this;}// 清除所有事件处理器void clear() {handlers_.clear();}private:// 存储事件处理函数template <typename Handler>void storeHandler(uint32_t key, Handler&& handler) {handlers_[key].emplace_back(std::forward<Handler>(handler));}private:uint32_t nextKey_;                                        // 唯一的事件处理函数键值std::map<uint32_t, std::vector<EventHandler>> handlers_;  // 存储事件处理函数,支持多个处理器
};//
// 事件处理函数1
void handler1(int data) {std::cout << "Handler1 received data: " << data << std::endl;
}// 事件处理函数2
void handler2(int data) {std::cout << "Handler2 received data: " << data << std::endl;
}int main() {// 使用 int 作为事件的键类型,事件处理函数是 std::function<void(int)>Events<std::function<void(int)>> events;// 注册两个事件处理器到同一个事件auto eventKey1 = events += handler1;auto eventKey2 = events += handler2;// 触发事件,所有绑定到该事件的处理器都会被调用events.trigger(100);  // 输出:// Handler1 received data: 100// Handler2 received data: 100// 移除事件处理器events -= eventKey1;std::cout << "After removing handler1:" << std::endl;// 再次触发事件,只有 handler2 会被调用events.trigger(200);  // 输出:Handler2 received data: 200return 0;
}

执行结果:

Handler1 received data: 100
Handler2 received data: 100
After removing handler1:
Handler2 received data: 200

2.4. 适配器模式的改进

适配器模式用于解决接口不兼容的问题。假设我们有一个基于激光雷达的数据接口,但我们需要将这个接口适配到自动驾驶控制系统。

#include <iostream>
#include <memory>class RadarSensor {public:void readData() {std::cout << "Reading data from Radar Sensor.\n";}
};class CameraSensor {public:void captureImage() {std::cout << "Capturing image from Camera Sensor.\n";}
};// 适配器模板
template <typename T>
class SensorAdapter {public:void operate() {static_assert(std::is_base_of<SensorAdapter, T>::value, "T must be derived from SensorAdapter");static_cast<T*>(this)->operateImpl();}
private:void operateImpl() = delete;
};class RadarAdapter : public SensorAdapter<RadarAdapter> {private:RadarSensor radar_;public:void operateImpl() {radar_.readData();}
};class CameraAdapter : public SensorAdapter<CameraAdapter> {private:CameraSensor camera_;public:void operateImpl() {camera_.captureImage();}
};int main() {RadarAdapter radarAdapter;CameraAdapter cameraAdapter;radarAdapter.operate();   // Reading data from Radar SensorcameraAdapter.operate();  // Capturing image from Camera Sensorreturn 0;
}

执行结果:

Reading data from Radar Sensor.
Capturing image from Camera Sensor.

2.5. 桥接模式的改进

在桥接模式中,模板类可以在编译时根据实现的类型来选择适当的操作,从而避免了虚函数的调用开销。

#include <iostream>
#include <memory>// 实现接口(通过模板类实现)
template <typename T>
class Implementor {public:void operationImpl() const {static_assert(std::is_base_of<Implementor, T>::value, "T must be derived from Implementor");static_cast<const T*>(this)->operationImpl();}
};// 具体实现A
class ConcreteImplementorA : public Implementor<ConcreteImplementorA> {public:void operationImpl() const {std::cout << "ConcreteImplementorA operation." << std::endl;}
};// 具体实现B
class ConcreteImplementorB : public Implementor<ConcreteImplementorB> {public:void operationImpl() const {std::cout << "ConcreteImplementorB operation." << std::endl;}
};// 抽象类,模板方法和组合
template <typename T>
class Abstraction {public:explicit Abstraction(std::shared_ptr<Implementor<T>> implementor) : implementor_(implementor) {}void operation() const {implementor_->operationImpl();  // 调用实现类的操作}protected:std::shared_ptr<Implementor<T>> implementor_;
};// 扩展抽象类A
template <typename T>
class RefinedAbstractionA : public Abstraction<T> {public:explicit RefinedAbstractionA(std::shared_ptr<Implementor<T>> implementor) : Abstraction<T>(implementor) {}void operation() const {std::cout << "RefinedAbstractionA operation: ";Abstraction<T>::operation();}
};// 扩展抽象类B
template <typename T>
class RefinedAbstractionB : public Abstraction<T> {public:explicit RefinedAbstractionB(std::shared_ptr<Implementor<T>> implementor) : Abstraction<T>(implementor) {}void operation() const {std::cout << "RefinedAbstractionB operation: ";Abstraction<T>::operation();}
};// 使用模板的桥接模式
int main() {// 创建具体实现auto implementorA = std::make_shared<ConcreteImplementorA>();auto implementorB = std::make_shared<ConcreteImplementorB>();// 创建抽象类A和BRefinedAbstractionA<ConcreteImplementorA> abstractionA(implementorA);RefinedAbstractionB<ConcreteImplementorB> abstractionB(implementorB);// 调用操作abstractionA.operation();  // 输出: RefinedAbstractionA operation: ConcreteImplementorA operation.abstractionB.operation();  // 输出: RefinedAbstractionB operation: ConcreteImplementorB operation.return 0;
}

执行结果:

RefinedAbstractionA operation: ConcreteImplementorA operation.
RefinedAbstractionB operation: ConcreteImplementorB operation.

2.6. 装饰器模式的改进

装饰器模式通过动态地组合不同的功能来扩展对象的行为。传统的装饰器模式依赖于继承和虚函数来实现动态行为的扩展,而模板化的装饰器模式通过组合和模板参数来实现类似的功能,避免了虚函数的调用和继承层次的复杂性。

#include <iostream>
#include <memory>// 基本组件接口
template <typename T>
class Component {public:virtual void operation() = 0;virtual ~Component() = default;
};// 具体组件
class ConcreteComponent : public Component<ConcreteComponent> {public:void operation() override {std::cout << "ConcreteComponent operation\n";}
};// 装饰器基类
template <typename T>
class Decorator : public Component<T> {protected:std::unique_ptr<Component<T>> component_;  // 装饰的组件public:explicit Decorator(std::unique_ptr<Component<T>> component) : component_(std::move(component)) {}void operation() override {component_->operation();}
};// 具体装饰器A
class ConcreteDecoratorA : public Decorator<ConcreteComponent> {public:explicit ConcreteDecoratorA(std::unique_ptr<Component<ConcreteComponent>> component): Decorator<ConcreteComponent>(std::move(component)) {}void operation() override {std::cout << "ConcreteDecoratorA operation\n";Decorator<ConcreteComponent>::operation();  // 调用原始组件的操作}
};// 具体装饰器B
class ConcreteDecoratorB : public Decorator<ConcreteComponent> {public:explicit ConcreteDecoratorB(std::unique_ptr<Component<ConcreteComponent>> component): Decorator<ConcreteComponent>(std::move(component)) {}void operation() override {std::cout << "ConcreteDecoratorB operation\n";Decorator<ConcreteComponent>::operation();  // 调用原始组件的操作}
};int main() {// 创建基本组件auto component = std::make_unique<ConcreteComponent>();// 使用装饰器A包装组件auto decoratorA = std::make_unique<ConcreteDecoratorA>(std::move(component));// 使用装饰器B包装装饰器Aauto decoratorB = std::make_unique<ConcreteDecoratorB>(std::move(decoratorA));// 调用最终的操作decoratorB->operation();return 0;
}

执行结果:

ConcreteDecoratorB operation
ConcreteDecoratorA operation
ConcreteComponent operation

2.7. 责任链模式的改进

责任链模式通过一系列的处理器对象来处理请求,每个处理器对象可以选择处理请求或将请求传递给下一个处理器。传统的责任链模式通常使用虚函数来决定请求的处理逻辑,而模板化的责任链模式通过模板和静态多态来替代虚函数

#include <iostream>
#include <memory>// 请求类型
struct Request {int value;explicit Request(int v) : value(v) {}
};// 处理器基类
class HandlerBase {public:virtual void handleRequest(Request& request) = 0;void setNext(std::shared_ptr<HandlerBase> next) {next_ = next;}protected:std::shared_ptr<HandlerBase> next_;
};// 具体处理器A
class ConcreteHandlerA : public HandlerBase {public:void handleRequest(Request& request) override {if (request.value < 10) {std::cout << "ConcreteHandlerA handled request with value " << request.value << std::endl;} else if (next_) {std::cout << "ConcreteHandlerA passing request to next handler." << std::endl;next_->handleRequest(request);}}
};// 具体处理器B
class ConcreteHandlerB : public HandlerBase {public:void handleRequest(Request& request) override {if (request.value >= 10 && request.value < 20) {std::cout << "ConcreteHandlerB handled request with value " << request.value << std::endl;} else if (next_) {std::cout << "ConcreteHandlerB passing request to next handler." << std::endl;next_->handleRequest(request);}}
};// 具体处理器C
class ConcreteHandlerC : public HandlerBase {public:void handleRequest(Request& request) override {std::cout << "ConcreteHandlerC handled request with value " << request.value << std::endl;}
};int main() {// 创建处理器实例auto handlerA = std::make_shared<ConcreteHandlerA>();auto handlerB = std::make_shared<ConcreteHandlerB>();auto handlerC = std::make_shared<ConcreteHandlerC>();// 设置责任链handlerA->setNext(handlerB);handlerB->setNext(handlerC);// 测试请求Request request1(5);handlerA->handleRequest(request1);Request request2(15);handlerA->handleRequest(request2);Request request3(25);handlerA->handleRequest(request3);return 0;
}

执行结果:

ConcreteHandlerA handled request with value 5
ConcreteHandlerA passing request to next handler.
ConcreteHandlerB handled request with value 15
ConcreteHandlerA passing request to next handler.
ConcreteHandlerB passing request to next handler.
ConcreteHandlerC handled request with value 25

http://www.ppmy.cn/ops/134766.html

相关文章

docker 安装之 windows安装

文章目录 1: 在Windows安装Docker报19044版本错误的时候&#xff0c;请大家下载4.24.1之前的版本&#xff08;含4.24.1&#xff09;2: Desktop-WSL kernel version too low3: docker-compose 安装 (v2.21.0)4: 配置镜像源 1: 在Windows安装Docker报19044版本错误的时候&#xf…

STM32 HAL 矩阵按键(轮询方式)

1、简介 最近准备做个门禁系统,首先通过4x4矩阵按键实现密码的设定,查看网上资料完成了4x4矩阵按键的初步使用,整理一个傻瓜式操作方便后续的使用与复习。 2、实物图 3、STM32CubeMX配置 4、KEY.C /******************************************************************…

7.揭秘C语言输入输出内幕:printf与scanf的深度剖析

揭秘C语言输入输出内幕&#xff1a;printf与scanf的深度剖析 C语言往期系列文章目录 往期回顾&#xff1a; VS 2022 社区版C语言的安装教程&#xff0c;不要再卡在下载0B/s啦C语言入门&#xff1a;解锁基础概念&#xff0c;动手实现首个C程序C语言概念之旅&#xff1a;解锁关…

NavVis VLX3的精度怎么去进行验证?【上海沪敖3D】

01、精度评价现状 三维捕捉行业还没有建立一个用于估算或验证移动激光扫描系统精度的统一标准。因此&#xff0c;需要高精度交付成果的专业人士很难相信设备所标注的精度规格&#xff0c;也就很难知道基于SLAM的移动激光扫描系统是否适合当前的项目。 NavVis将通过展示一种严格…

学习日志011--模块,迭代器与生成器,正则表达式

一、python模块 在之前学习c语言时&#xff0c;我们学了分文件编辑&#xff0c;那么在python中是否存在类似的编写方式&#xff1f;答案是肯定的。python中同样可以实现分文件编辑。甚至还有更多的好处&#xff1a; ‌提高代码的可维护性‌&#xff1a;当代码被分成多个文件时…

系统级编程语言Rust概述

文章目录 语言背景和历史基本语法和结构语言特性标准库和生态系统工具链和开发环境rustccargo 性能及应用场景语言的优缺点对比其他编程语言总结学习资料 语言背景和历史 Rust是由Mozilla的工程师Graydon Hoare于2006年开始设计的一门编程语言&#xff0c;目的是创建一种内存安…

开源vs闭源:你更看好哪一方?

开源vs闭源&#xff1a;你更看好哪一方&#xff1f; 引言 你有没有想过&#xff0c;在当今人工智能&#xff08;AI&#xff09;蓬勃发展的时代&#xff0c;开源大模型与闭源大模型之间的竞争竟然如此激烈&#xff1f;这仿佛就像是两位骑士在同一片战场上&#xff0c;争夺你我…

MySQL 数据类型

数值类型 int类型 类型说明tinyint1字节&#xff0c;范围从-128到127&#xff08;有符号&#xff09;&#xff0c;0到255&#xff08;无符号&#xff09;smallint2字节&#xff0c;范围从-2^15到2^15-1&#xff08;有符号&#xff09;&#xff0c;0到2^16-1&#xff08;无符号…