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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:45:57