C++模拟模板虚函数:简化设备类代码并保留基类抽象
简化带多态要求的设备类读写代码方案
核心思路
针对你遇到的重复switch代码、需区分读写操作、保留基类多态的需求,核心解决方向是:
- 把位宽相关的读写逻辑抽离成通用工具,避免每个子类重复实现switch
- 保留Device基类的虚函数接口,确保兼容
std::vector<Device*>的多态容器 - 允许子类自定义读写前的设备特定操作,不侵入通用逻辑
方案1:通用工具函数抽离
先实现一套位宽分发的通用工具,把重复的类型转换和switch逻辑集中处理:
#include <array> #include <cstdint> #include <type_traits> namespace detail { // 通用字节转类型的加载函数 template<typename T> T load_from_bytes(const uint8_t* data, uint64_t addr) { return *reinterpret_cast<const T*>(data + addr); } // 通用类型转字节的存储函数 template<typename T> void store_to_bytes(uint8_t* data, uint64_t addr, uint64_t value) { *reinterpret_cast<T*>(data + addr) = static_cast<T>(value); } // 位宽到类型的映射处理器 struct LoadHandler { const uint8_t* data; uint64_t addr; uint64_t operator()(std::integral_constant<size_t, 8>) const { return load_from_bytes<uint8_t>(data, addr); } uint64_t operator()(std::integral_constant<size_t, 16>) const { return load_from_bytes<uint16_t>(data, addr); } uint64_t operator()(std::integral_constant<size_t, 32>) const { return load_from_bytes<uint32_t>(data, addr); } uint64_t operator()(std::integral_constant<size_t, 64>) const { return load_from_bytes<uint64_t>(data, addr); } }; struct StoreHandler { uint8_t* data; uint64_t addr; uint64_t value; void operator()(std::integral_constant<size_t, 8>) const { store_to_bytes<uint8_t>(data, addr, value); } void operator()(std::integral_constant<size_t, 16>) const { store_to_bytes<uint16_t>(data, addr, value); } void operator()(std::integral_constant<size_t, 32>) const { store_to_bytes<uint32_t>(data, addr, value); } void operator()(std::integral_constant<size_t, 64>) const { store_to_bytes<uint64_t>(data, addr, value); } }; // 位宽分发入口,统一处理switch逻辑 template<typename Handler> auto dispatch_by_size(uint64_t size, Handler&& handler) { switch(size) { case 8: return handler(std::integral_constant<size_t, 8>{}); case 16: return handler(std::integral_constant<size_t, 16>{}); case 32: return handler(std::integral_constant<size_t, 32>{}); case 64: return handler(std::integral_constant<size_t, 64>{}); default: if constexpr(std::is_same_v<decltype(handler(std::integral_constant<size_t, 8>{})), uint64_t>) return uint64_t{0}; else return; } } }
然后修改设备类,复用通用工具:
// 基类保持原接口,补充虚析构避免内存泄漏 class Device { protected: int base; int size; // 其他成员 public: virtual uint64_t load(uint64_t addr, uint64_t size) = 0; virtual void store(uint64_t addr, uint64_t value, uint64_t size) = 0; virtual ~Device() = default; }; // Dram子类,简化读写实现 class Dram : public Device { // 注意:必须public继承才能支持多态 private: std::array<uint8_t, 1024> data; // 设备特定的前置操作 void ram_specific_load() { /* 原逻辑保留 */ } void ram_specific_store() { /* 原逻辑保留 */ } public: uint64_t load(uint64_t addr, uint64_t size) override { ram_specific_load(); return detail::dispatch_by_size(size, detail::LoadHandler{data.data(), addr}); } void store(uint64_t addr, uint64_t value, uint64_t size) override { ram_specific_store(); detail::dispatch_by_size(size, detail::StoreHandler{data.data(), addr, value}); } };
方案2:CRTP辅助基类(进一步减少子类代码)
如果有多个设备子类,可使用CRTP做辅助基类,把load/store的通用逻辑统一实现,子类只需要提供数据和前置操作:
// CRTP辅助基类,继承自Device确保多态兼容性 template<typename Derived> class DeviceHelper : public Device { protected: // 让子类暴露私有数据和操作 uint8_t* get_data() { return static_cast<Derived*>(this)->data.data(); } const uint8_t* get_data() const { return static_cast<Derived*>(this)->data.data(); } void pre_load() { static_cast<Derived*>(this)->ram_specific_load(); } void pre_store() { static_cast<Derived*>(this)->ram_specific_store(); } public: uint64_t load(uint64_t addr, uint64_t size) override { pre_load(); return detail::dispatch_by_size(size, detail::LoadHandler{get_data(), addr}); } void store(uint64_t addr, uint64_t value, uint64_t size) override { pre_store(); detail::dispatch_by_size(size, detail::StoreHandler{get_data(), addr, value}); } }; // Dram子类,仅需实现设备特定逻辑 class Dram : public DeviceHelper<Dram> { friend class DeviceHelper<Dram>; // 允许辅助基类访问私有成员 private: std::array<uint8_t, 1024> data; void ram_specific_load() { /* 原逻辑 */ } void ram_specific_store() { /* 原逻辑 */ } };
修正后的使用代码
注意原代码中push_back(&Dram())会导致悬空指针(临时对象销毁),改用智能指针管理内存:
#include <vector> #include <memory> std::vector<std::unique_ptr<Device>> devices; devices.emplace_back(std::make_unique<Dram>()); devices.emplace_back(std::make_unique<Gpu>()); // 其他设备类同理 // 原使用逻辑完全兼容 devices[1]->store(0x1234, devices[0]->load(0x4321, 64), 64); uint32_t var = static_cast<uint32_t>(devices[1]->load(0x1212, 32));
方案优势
- 消除重复代码:所有位宽相关的switch和类型转换逻辑集中在detail工具中,子类无需重复实现
- 保留多态性:完全兼容
std::vector<Device*>(或智能指针容器)的抽象管理 - 灵活扩展:子类可自由定义读写前的设备特定操作,不影响通用逻辑
- 类型安全:集中处理
reinterpret_cast,避免分散的类型转换错误
内容的提问来源于stack exchange,提问作者Bartek_0x00
相关产品推荐
相关产品推荐

