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

如何在不使用指针的情况下实现基于引用的AST构建函数?

问题

我有一个采用访问者模式的抽象语法树(AST)类,所有构造函数与visit函数均接收抽象节点ASTNode或其派生类的引用,相关类定义如下:

class ASTNode {
public:
    virtual void accept(ASTVisitor& visitor) = 0;
};

class NameNode : public ASTNode {
public:
    NameNode(std::string name) : name(name) { }

    void accept(ASTVisitor& visitor) override;
    
    std::string name;
};

class ListNode : public ASTNode { 
public:
    ListNode(ElementSptr list) :  list(list) { }

    void accept(ASTVisitor& visitor) override;

    ElementSptr list;
};


class BinaryOperator : public ASTNode {
public:
    BinaryOperator(std::string kind, ASTNode& left, ASTNode& right)
    : kind(kind), left(left), right(right) { }

    void accept(ASTVisitor& visitor) override;

    std::string kind;
    ASTNode& left;
    ASTNode& right;
};

class ASTVisitor {
    public:
    virtual void visit(ASTNode& node) = 0;
    virtual void visit(NameNode& name) = 0;
    virtual void visit(ListNode& list) = 0;
    virtual void visit(BinaryOperator& bOp) = 0;
    virtual void visit(UnaryOperator& uOp) = 0;
};

我通过独立的TreeBuilder类实现树的构建,其buildTree函数如下:

ASTNode& TreeBuilder::buildTree(std::string expression){
    std::vector<std::string> tokens = split(expression);
    std::deque<ASTNode&> nodeStack;;
    std::deque<std::string> operatorStack;

    ASTNode root;
    ASTNode& left;
    ASTNode& right;

//generates tree from tokens 
 
return root;


}

由于ASTNode是抽象类,无法直接实例化;定义引用时又提示需初始化。使用指针可解决该问题,但现有树结构依赖引用,无法兼容指针实现。请问是否存在不使用指针即可实现该函数的方法?该函数需返回根节点,供调用类操作其子节点。

解决方案

不使用裸指针的前提下,可通过以下几种方式解决问题:

  • 用智能指针管理节点生命周期,对外返回引用
    在TreeBuilder内部用std::unique_ptr或std::shared_ptr存储AST节点,确保对象生命周期覆盖调用方使用引用的周期。比如将智能指针存入TreeBuilder的成员变量,构建节点时:

    std::unique_ptr<BinaryOperator> bop = std::make_unique<BinaryOperator>("+", *leftNodeRef, *rightNodeRef);
    auto& rootRef = *bop;
    this->nodeStorage.push_back(std::move(bop));
    

    最后返回rootRef即可,只要nodeStorage的生命周期足够长,引用就始终有效。

  • 利用std::variant存储具体节点类型
    定义变体类型统一存储所有AST节点的具体实例:

    using ASTNodeVariant = std::variant<NameNode, ListNode, BinaryOperator>;
    

    将TreeBuilder中的nodeStack替换为std::deque<ASTNodeVariant>,构建节点时直接创建具体类型的实例存入变体,后续通过std::get取出引用传递给构造函数。这种方式能在栈或容器中管理节点,避免直接使用指针,但需注意变体的生命周期要覆盖引用的使用周期。

  • 实现AST节点对象池
    创建一个对象池类负责在堆上创建并管理AST节点的生命周期,TreeBuilder从池内获取节点引用:

    class ASTNodePool {
    public:
        template<typename T, typename... Args>
        T& create(Args&&... args) {
            auto ptr = std::make_unique<T>(std::forward<Args>(args)...);
            auto& ref = *ptr;
            nodes.push_back(std::move(ptr));
            return ref;
        }
    private:
        std::vector<std::unique_ptr<ASTNode>> nodes;
    };
    

    在TreeBuilder中持有ASTNodePool实例,构建节点时调用pool.create<BinaryOperator>(...)获取引用,返回的根节点引用也来自池,只要池的生命周期足够长,引用就有效。

核心注意点:引用必须绑定到生命周期足够长的对象上,不能绑定到临时对象或会被提前销毁的栈对象。无论采用哪种方式,都要确保调用方使用根节点及其子节点引用期间,对应的对象不会被销毁。

内容的提问来源于stack exchange,提问作者callum arul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 22:35:21