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

如何定义由派生类指定数据类型的C++抽象基类?

实现由派生类指定数据类型的基类

嘿,看起来你想要构建一个让派生类决定核心数据类型的基类,这种场景在C++里很常见,我给你两种实用的实现方案,结合你的伪代码来拆解:

方案1:模板基类(最直观的多态方案)

把基类设计为模板类,让派生类在继承时明确指定FeatureType1和FeatureType2的类型,同时派生类需要实现基类声明的纯虚方法来完成具体逻辑。

#include <vector>
#include <utility>
#include <cmath>

// 模板基类,由派生类指定两种特征类型
template<typename FeatureType1, typename FeatureType2>
class Base {
public:
    void Enroll(std::vector<int> v) {
        // 调用派生类的特征提取方法,存入列表
        feature_list.emplace_back(ExtractFeature1(std::move(v)));
    }

    std::vector<double> Compare(std::vector<int> v) {
        FeatureType2 ft2 = ExtractFeature2(std::move(v));
        std::vector<double> scores;
        scores.reserve(feature_list.size()); // 提前分配内存优化性能
        
        for (auto& ft1 : feature_list) {
            scores.emplace_back(CompareFeatures(ft1, ft2));
        }
        return scores;
    }

protected:
    std::vector<FeatureType1> feature_list;

    // 纯虚方法:派生类必须实现的核心逻辑
    virtual FeatureType1 ExtractFeature1(std::vector<int> v) = 0;
    virtual FeatureType2 ExtractFeature2(std::vector<int> v) = 0;
    virtual double CompareFeatures(const FeatureType1& ft1, const FeatureType2& ft2) = 0;
};

// 派生类示例:指定特征类型并实现具体逻辑
#include <array>
class Derived : public Base<std::vector<double>, std::array<double, 10>> {
protected:
    FeatureType1 ExtractFeature1(std::vector<int> v) override {
        FeatureType1 feat;
        for (int num : v) {
            feat.push_back(static_cast<double>(num) / 100.0);
        }
        return feat;
    }

    FeatureType2 ExtractFeature2(std::vector<int> v) override {
        FeatureType2 feat{};
        for (size_t i = 0; i < v.size() && i < feat.size(); ++i) {
            feat[i] = static_cast<double>(v[i]) * 2.0;
        }
        return feat;
    }

    double CompareFeatures(const FeatureType1& ft1, const FeatureType2& ft2) override {
        double sum = 0.0;
        size_t min_size = std::min(ft1.size(), ft2.size());
        for (size_t i = 0; i < min_size; ++i) {
            sum += std::abs(ft1[i] - ft2[i]);
        }
        return sum / min_size;
    }
};

方案2:CRTP(奇异递归模板模式,无虚函数开销)

如果你追求极致性能,不想有虚函数的运行时开销,可以用CRTP。这种方式让基类在编译期就绑定派生类的方法,完全不需要虚表。

#include <vector>
#include <utility>
#include <cmath>

// CRTP基类:把派生类作为模板参数传入
template<typename Derived>
class BaseCRTP {
public:
    void Enroll(std::vector<int> v) {
        // 直接调用派生类的方法(编译期确定)
        feature_list.emplace_back(derived()->ExtractFeature1(std::move(v)));
    }

    std::vector<double> Compare(std::vector<int> v) {
        using FeatureType2 = typename Derived::FeatureType2;
        FeatureType2 ft2 = derived()->ExtractFeature2(std::move(v));
        std::vector<double> scores;
        scores.reserve(feature_list.size());
        
        for (auto& ft1 : feature_list) {
            scores.emplace_back(derived()->CompareFeatures(ft1, ft2));
        }
        return scores;
    }

protected:
    // 从派生类获取特征类型别名
    using FeatureType1 = typename Derived::FeatureType1;
    std::vector<FeatureType1> feature_list;

private:
    // 安全转换为派生类指针(CRTP核心)
    Derived* derived() {
        return static_cast<Derived*>(this);
    }

    const Derived* derived() const {
        return static_cast<const Derived*>(this);
    }
};

// 派生类示例:必须定义特征类型别名和核心方法
#include <array>
class DerivedCRTP : public BaseCRTP<DerivedCRTP> {
public:
    // 定义基类需要的特征类型
    using FeatureType1 = std::vector<double>;
    using FeatureType2 = std::array<double, 10>;

    FeatureType1 ExtractFeature1(std::vector<int> v) {
        FeatureType1 feat;
        for (int num : v) {
            feat.push_back(static_cast<double>(num) / 100.0);
        }
        return feat;
    }

    FeatureType2 ExtractFeature2(std::vector<int> v) {
        FeatureType2 feat{};
        for (size_t i = 0; i < v.size() && i < feat.size(); ++i) {
            feat[i] = static_cast<double>(v[i]) * 2.0;
        }
        return feat;
    }

    double CompareFeatures(const FeatureType1& ft1, const FeatureType2& ft2) {
        double sum = 0.0;
        size_t min_size = std::min(ft1.size(), ft2.size());
        for (size_t i = 0; i < min_size; ++i) {
            sum += std::abs(ft1[i] - ft2[i]);
        }
        return sum / min_size;
    }
};

两种方案怎么选?

  • 模板基类:支持运行时多态(比如可以用Base*指向不同派生类对象),灵活性高,但有虚函数的微小开销。适合需要动态切换特征逻辑的场景。
  • CRTP:编译期绑定方法,完全没有虚函数开销,性能更好,但不支持运行时多态,所有类型必须在编译期确定。适合性能敏感、逻辑固定的场景。

内容的提问来源于stack exchange,提问作者D R

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:28:22