C++11抽象类如何实现接收迭代器参数的虚read、write方法?
方案对比与选型建议
C++11确实不支持模板虚函数,你提出的两种方案各有适用场景,没有绝对的「更规范」,可根据业务需求选择:
方案1:弃用抽象类,采用模板约定(静态多态)
该方案本质是用编译期静态多态替代运行时动态多态,适用场景:
- 所有read/write调用的实现类类型、迭代器类型都可以在编译期确定
- 不需要用基类指针统一持有不同实现类的实例,不需要运行时动态派发接口调用
优势: - 完全保留迭代器的泛型能力,支持任意迭代器类型(原生指针、标准容器迭代器、自定义迭代器均可兼容)
- 无虚函数调用的运行时开销
- 不需要提前枚举所有支持的迭代器类型
注意点:
可以配合C++11的static_assert和类型萃取做编译期接口校验,解决「口头约定」没有强制约束力的问题,示例参考:
template<typename Impl> class StorageAccessor { public: template<typename Iterator> void read(uint64_t adr, Iterator begin, Iterator end) const { // 编译期校验实现类是否有符合要求的read方法 static_assert(std::is_same< decltype(std::declval<const Impl>().read(adr, begin, end)), void >::value, "Impl must implement read(uint64_t, Iterator, Iterator) const"); impl_.read(adr, begin, end); } // write方法逻辑同理 private: Impl impl_; };
方案2:抽象类本身改为模板类,绑定迭代器类型
该方案保留了运行时动态多态能力,适用场景:
- 需要用基类指针在运行时持有不同的实现类实例,必须动态派发read/write调用
- 用到的迭代器类型非常固定,不需要支持任意迭代器
劣势: - 每一种迭代器类型对应一个独立的基类实例,不同迭代器类型的基类无法通用,如果需要支持多种迭代器,代码膨胀会非常明显
- 后续新增迭代器类型时,需要重新实例化整个抽象类继承链
工程常用折衷方案
如果以上两种方案都不能满足需求,还有一种兼容性更强的实现方式:擦除迭代器类型,抽象基类只保留字节级纯虚接口,再封装非虚的泛型迭代器方法:
class AbstractStorage { public: virtual ~AbstractStorage() = default; // 非虚的泛型迭代器接口,对外暴露 template<typename Iterator> void read(uint64_t adr, Iterator begin, Iterator end) const { size_t byte_len = std::distance(begin, end) * sizeof(typename std::iterator_traits<Iterator>::value_type); read_bytes(adr, reinterpret_cast<uint8_t*>(&(*begin)), byte_len); } template<typename Iterator> void write(uint64_t adr, Iterator begin, Iterator end) const { size_t byte_len = std::distance(begin, end) * sizeof(typename std::iterator_traits<Iterator>::value_type); write_bytes(adr, reinterpret_cast<const uint8_t*>(&(*begin)), byte_len); } protected: // 纯虚字节级接口,由派生类实现 virtual void read_bytes(uint64_t adr, uint8_t* buf, size_t len) const = 0; virtual void write_bytes(uint64_t adr, const uint8_t* buf, size_t len) const = 0; };
该方案同时保留了动态多态能力和泛型迭代器支持,只要迭代器指向的是可直接内存拷贝的平凡类型,完全可以满足需求,是工业界最常用的实现方式。
最终选型参考
- 不需要运行时多态的场景优先选方案1,更符合C++泛型编程设计思想,灵活度最高
- 必须要运行时多态的场景优先选上面的折衷方案,比方案2适用范围广很多
- 只有迭代器类型完全固定、且无法用字节擦除的特殊场景,才考虑方案2
内容的提问来源于stack exchange,提问作者yannick818
相关产品推荐
相关产品推荐

