文章目录
0. 引言
MISRA C++ 标准对类型转换和虚函数使用做出严格规定,尤其在嵌入式系统中,以确保代码的可预测性和高效性。传统设计模式中虚函数和多态性引入会增加运行时开销,增加系统负担。然而,模板编程通过编译时决策代替运行时动态绑定,可以提升性能并降低内存消耗。
本文将尝试使用C++11改进常用的几个设计模式。
1. 模板编程替换虚函数和多态的必要性
1.1. MISRA C++对类型转换和虚函数的规定
MISRA C++ 标准对于类型转换和虚函数的有严格的使用要求,要求包括:
- Rule 5-0-1(MISRA C++ 2008):禁止使用不安全的类型转换,如
reinterpret_cast
、const_cast
、static_cast
和dynamic_cast
。 - Rule 5-0-2:要求类型转换必须明确指定转换类型,确保转换符合预期行为,避免使用不安全的转换方式。
- Rule 5-1-1:在进行类型转换时,必须确保转换类型合法,不破坏对象的常量性或类型。
- 运行时开销:虚函数表(vtable)的查找会带来额外的运行时开销,增加程序的资源需求。
- RTTI(运行时类型信息)开销:启用 RTTI 会增加内存使用,尤其是在
dynamic_cast
或typeid
被使用时。 - MISRA C++的限制:标准要求避免使用虚函数和多态机制,特别是在没有严格控制的情况下,可能会导致代码行为不可预测,增加出错的风险。
1.2. 模板编程的优势:替代虚函数和多态机制
模板编程通过在编译时绑定函数和类,避免运行时的虚函数查找和多态性问题。使用模板编程的优势如下:
- 编译时绑定:模板代码在编译时就确定了函数调用和对象行为,避免了虚函数表的查找。
- 内联优化:编译器能够对模板代码进行内联优化,直接将模板代码嵌入到调用点。
- 提高执行效率:由于没有动态绑定和运行时类型信息,模板编程能够显著降低程序的运行时开销,提升性能。
模板编程避免了虚函数和多态机制的开销,并且在遵循 MISRA C++ 标准时,能够更容易地实现高效且符合规范的设计模式。
2. 设计模式改进
模板编程 和 静态多态 通过在编译时进行决策,避免了虚函数调用和动态绑定的需要。以下是一些常见设计模式的改进方式,尽量在不违背设计原则的情况下提升代码的性能。
2.1. 单例模式的改进
传统的单例模式通过 static
变量和线程同步机制来保证只创建一个实例。然而,如果使用虚函数或者多态,在某些情况下可能会影响线程安全性,增加程序的复杂度。在 C++11/14 中,可以利用 std::once_flag
和 std::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