当前位置: 首页 > news >正文

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

文章目录

    • 0. 引言
    • 1. 模板编程替换虚函数和多态的必要性
      • 1.1. MISRA C++对类型转换和虚函数的规定
      • 1.2. 虚函数与多态问题的影响及如何适应MISRA C++要求
      • 1.3. 模板编程的优势:替代虚函数和多态机制
    • 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++要求

虚函数和多态机制在嵌入式系统中,往往面临以下问题:

  • 运行时性能问题:虚函数表查找、RTTI机制的引入,都会增加程序的运行时开销,尤其是在资源受限的嵌入式系统中,可能会显著影响系统性能。
  • 内存开销:启用 RTTI 会使每个多态类占用额外的内存,这对于内存有限的嵌入式系统是一个不小的负担。
  • MISRA C++的限制:虚函数和多态机制的使用在 MISRA C++ 中受到限制,因为它们增加了代码的复杂性,降低了可预测性,并且可能违反一些规则,如避免不安全的类型转换。

为了适应 MISRA C++ 的要求,可以考虑以下几点:

  • 避免虚函数和多态:尽量避免使用运行时多态和虚函数,减少 RTTI 开销,避免使用 dynamic_casttypeid
  • 设计时决策:通过明确的类型接口和结构体来实现类型转换,确保每个类型转换都能被清晰审查和验证。
  • 使用强类型系统:避免使用 void* 等不明确类型的指针,确保类型清晰,从而减少不必要的类型转换。

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

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

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

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

2. 设计模式改进

在传统的设计模式中,许多模式(如单例模式、工厂模式、观察者模式等)依赖于虚函数和继承来实现多态和灵活的行为切换。然而,这种方式虽然灵活,但会带来运行时的开销,包括虚函数表查找、内存使用的增加以及与运行时类型信息(RTTI)相关的性能损失。

模板编程静态多态 通过在编译时进行决策,避免了虚函数调用和动态绑定的需要。通过使用模板,我们可以在不牺牲灵活性的情况下,提高代码的执行效率,并且能够使代码更加符合 MISRA C++ 等标准的要求。

以下是一些常见设计模式的改进方式,尽量在不违背设计原则的情况下提升代码的性能。

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.mrgr.cn/news/76378.html

相关文章:

  • 【阅读记录-章节2】Build a Large Language Model (From Scratch)
  • pgSQL-timescaledb复制表出现的问题
  • ODOO学习笔记(6):Odoo与Oracle ERP的区别
  • 猫头虎分享: 小米大模型升级第二代MiLM2:从一代到二代,能力飞跃提升
  • 为什么要使用Ansible实现Linux管理自动化?
  • ⚡️如何在 React 和 Next.js 项目里优雅的使用 Zustand
  • 二分搜索的三种方法
  • 正则表达式(补充)
  • vue3: ref, reactive, readonly, shallowReactive
  • uniapp: 微信小程序包体积超过2M的优化方法
  • Vue和Vue-Element-Admin(十三):基于vue2比较学习vue3
  • 【AIGC】如何通过ChatGPT提示词Prompt定制个性学习计划
  • vue3: toRef, reactive, toRefs, toRaw
  • linux下编译安装memcached
  • Android ART知多少?
  • 《抽象类和接口》
  • 渗透测试之信息收集 DNS主机发现探测方式NetBIOS 协议发现主机 以及相关PorCheck scanline工具的使用哟
  • 跳房子(弱化版)
  • 芯原科技嵌入式面试题及参考答案
  • cMake编译github中源码
  • flink cdc 应用
  • 重建大师跑空三,出现进度条回退是什么原因?
  • 城市轨道交通数据可视化的应用与优势
  • Intelligent Transportation Scheduling
  • IT框架与库:理解它们的不同与共同点
  • 用友YonBIP-R5旗舰版 yonbiplogin 任意文件读取漏洞复现