如何定义由派生类指定数据类型的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
相关产品推荐
相关产品推荐

