C++中带模板子类的Manager类替代方案咨询
我定义了一个作为其他类型基类的Expression模板类:
template <typename T> class Expression : public ExpressionProxy { public: ExpressionType type; T value; Expression(ExpressionType type, T value) { this->type = type; this->value = value; } virtual T GetValue() { return this->value; } virtual ExpressionType GetType() { return this->type; } };
例如IntegerExpression的实现:
class IntegerExpression: public Expression<int> { public: IntegerExpression(int value) : Expression<int>(ExpressionType::Integer, value) { } };
这么设计是为了实现可互换的值类型(比如IntegerExpression返回int,FractionExpression返回float等),同时能处理SumExpression这类复杂表达式,并且通过ExpressionType来判断表达式类型,避免向下转型或使用typeid。
但使用模板后,我的Manager类无法正常工作,以下是简化的演示代码(无法运行):
std::vector<Expression> controlledComponents; ExpressionManager() { this->controlledComponents.reserve(100000); } Expression* AddNewControlledComponent(Expression args) { if ((int) this->controlledComponents.size() >= this->maxElement) { std::cerr << "Manager: Overflow. Please resize array." << '\n'; return nullptr; } this->controlledComponents.push_back(args); return &this->controlledComponents[this->controlledComponents.size()-1]; } Expression* GetControlledComponent(int idx) { if (idx < 0 || idx >= (int) this->controlledComponents.size()) { return nullptr; } return &this->controlledComponents[idx]; } const static int maxElement = 100000;
(注:我知道应该用动态对象池和智能指针,暂时忽略这一点。)
问题在于Expression是模板类,必须指定类型参数才能声明。我可以为每种类型单独创建Manager<T>,但类型太多的话这种方案不现实;也可以让Expression继承空的ExpressionProxy基类,但这样会失去模板类的意义。
简言之:如果不用模板实现Expression,派生类获取自定义类型值时必须向下转型(或者用std::string、std::vector这类通用类型,效果很差);如果用模板,又没法统一存储和使用Expression实例。
核心思路是用非模板的抽象基类统一接口,模板类作为中间层实现类型安全的GetValue,既能统一存储,又避免不必要的转型。
步骤1:定义非模板抽象基类
先做一个不带模板的ExpressionBase,只声明纯虚函数作为所有表达式的统一接口:
enum class ExpressionType { Integer, Fraction, Sum }; class ExpressionBase { public: virtual ~ExpressionBase() = default; // 必须有虚析构,避免内存泄漏 virtual ExpressionType GetType() const = 0; };
步骤2:改造模板Expression类
让模板Expression继承ExpressionBase,同时保留类型安全的GetValue:
template <typename T> class Expression : public ExpressionBase { public: ExpressionType type; T value; Expression(ExpressionType type, T value) : type(type), value(value) {} T GetValue() const { return value; } ExpressionType GetType() const override { return type; } };
步骤3:调整派生类
比如IntegerExpression保持原有写法即可:
class IntegerExpression : public Expression<int> { public: IntegerExpression(int value) : Expression<int>(ExpressionType::Integer, value) {} };
步骤4:修复Manager类
现在Manager可以存储ExpressionBase*(这里按要求用原始指针演示,实际建议用智能指针):
class ExpressionManager { private: std::vector<ExpressionBase*> controlledComponents; const static int maxElement = 100000; public: ExpressionManager() { controlledComponents.reserve(maxElement); } ExpressionBase* AddNewControlledComponent(ExpressionBase* expr) { if (controlledComponents.size() >= maxElement) { std::cerr << "Manager: Overflow. Please resize array." << '\n'; return nullptr; } controlledComponents.push_back(expr); return expr; } ExpressionBase* GetControlledComponent(int idx) { if (idx < 0 || idx >= static_cast<int>(controlledComponents.size())) { return nullptr; } return controlledComponents[idx]; } // 析构函数清理内存,避免泄漏 ~ExpressionManager() { for (auto expr : controlledComponents) { delete expr; } } };
使用示例
结合ExpressionType做安全转型,获取具体类型的值:
int main() { ExpressionManager manager; manager.AddNewControlledComponent(new IntegerExpression(42)); ExpressionBase* expr = manager.GetControlledComponent(0); if (expr->GetType() == ExpressionType::Integer) { // 已通过GetType确认类型,转型安全 auto intExpr = static_cast<Expression<int>*>(expr); std::cout << intExpr->GetValue() << std::endl; // 输出42 } return 0; }
替代方案:使用std::variant(C++17及以上)
如果不想用继承体系,可以用std::variant存储不同类型的表达式,Manager直接存储std::vector<std::variant<...>>,通过std::visit处理不同类型:
#include <variant> using ExprVariant = std::variant<IntegerExpression, FractionExpression>; class ExpressionManager { private: std::vector<ExprVariant> controlledComponents; const static int maxElement = 100000; public: bool AddNewControlledComponent(const ExprVariant& expr) { if (controlledComponents.size() >= maxElement) { std::cerr << "Manager: Overflow. Please resize array." << '\n'; return false; } controlledComponents.push_back(expr); return true; } void PrintValue(int idx) { if (idx < 0 || idx >= static_cast<int>(controlledComponents.size())) return; std::visit([](const auto& expr) { std::cout << expr.GetValue() << std::endl; }, controlledComponents[idx]); } };
这种方案更简洁,但需要提前枚举所有可能的表达式类型,适合类型数量固定的场景。
内容的提问来源于stack exchange,提问作者silverfox

