C++如何将成员类型、构造函数不同的两个类改写为继承或模板类
问题描述
现有如下C++代码,定义了A、B两个类:
- 两个类的成员函数
func实现逻辑完全相同 - 成员变量
q的类型不同:A中是queue<Data>,B中是带自定义比较器的priority_queue - 构造函数实现存在差异:A的
q可默认构造,B的q构造时需要传入比较器对象
需求是仅编写一次func的代码,确认是否可以通过「让A、B继承同一基类,将func放在基类实现」,或者「将二者改写为模板类」的方式实现,以及具体实现方法。
原始代码如下:
class Data { public: int i; }; auto cmp = [](const Data& d1, const Data& d2) { return d1.i > d2.i; }; class A { private: queue<Data> q; public: A() {}; void func() { int cnt = 0; while (!q.empty()) { std::cout << cnt++ << std::endl; q.pop(); } } }; class B { private: priority_queue<Data, vector<Data>, decltype(cmp)> q; public: B() :q(cmp) {}; void func() { int cnt = 0; while (!q.empty()) { std::cout << cnt++ << std::endl; q.pop(); } } };
实现方案
两种思路都可以实现,具体实现方式如下:
方案1:模板类实现(优先选择)
这是该场景下开销最低、冗余代码最少的方案。func的逻辑仅依赖队列对象的empty()、pop()两个通用接口,不需要提前约束类型,编译器会在模板实例化阶段自动校验类型是否支持对应操作,无运行时额外开销。
实现时抽离一个模板基类承载公共func逻辑,通过完美转发适配不同类型队列的初始化参数要求,A、B直接继承对应实例化的模板基类即可:
#include <iostream> #include <queue> #include <vector> #include <utility> class Data { public: int i; }; auto cmp = [](const Data& d1, const Data& d2) { return d1.i > d2.i; }; template <typename QueueType> class CommonBase { protected: QueueType q; public: // 可变参数完美转发,兼容不同队列的构造参数需求 template <typename... CtorArgs> CommonBase(CtorArgs&&... args) : q(std::forward<CtorArgs>(args)...) {} // 公共func逻辑仅在此处实现一次 void func() { int cnt = 0; while (!q.empty()) { std::cout << cnt++ << std::endl; q.pop(); } } }; // A类对应普通队列,默认构造即可 class A : public CommonBase<std::queue<Data>> { public: A() : CommonBase() {} }; // B类对应优先队列,构造时传入比较器 class B : public CommonBase<std::priority_queue<Data, std::vector<Data>, decltype(cmp)>> { public: B() : CommonBase(cmp) {} };
该方案的特点:
- 无虚函数运行时开销,所有类型绑定在编译期完成
- 公共逻辑完全复用,后续修改
func仅需调整一处 - 构造逻辑自动适配,不需要为不同队列单独写冗余的初始化包装代码
方案2:传统继承+多态实现
如果需要通过基类指针/引用统一管理A、B对象,可以使用传统面向对象的多态方案。将func放在基类作为公共非虚接口,把不同队列的底层操作抽象为纯虚函数由派生类实现:
#include <iostream> #include <queue> #include <vector> class Data { public: int i; }; auto cmp = [](const Data& d1, const Data& d2) { return d1.i > d2.i; }; class Base { private: // 派生类实现的底层操作接口 virtual bool check_empty() = 0; virtual void pop_element() = 0; public: virtual ~Base() = default; // 公共func逻辑仅在基类实现一次 void func() { int cnt = 0; while (!check_empty()) { std::cout << cnt++ << std::endl; pop_element(); } } }; class A : public Base { private: std::queue<Data> q; bool check_empty() override { return q.empty(); } void pop_element() override { q.pop(); } public: A() = default; }; class B : public Base { private: std::priority_queue<Data, std::vector<Data>, decltype(cmp)> q; bool check_empty() override { return q.empty(); } void pop_element() override { q.pop(); } public: B() : q(cmp) {} };
该方案的特点:
- 符合传统OOP继承体系,支持运行时多态
- 存在虚函数调用的微小运行时开销
- 每个派生类需要实现薄包装的底层接口函数,存在极少量重复代码
内容的提问来源于stack exchange,提问作者f1msch
相关产品推荐
相关产品推荐

