You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 17:21:26