<p>表达式树节点必须用动态分配(new)配合裸指针或智能指针定义,不可用栈上对象地址;struct Node需含char op、double val、Node left和Node right,运算符节点val无效、数值节点左右子为空,析构须递归delete子树。</p>

表达式树节点怎么用指针定义
必须用动态分配(new)配合裸指针或智能指针,不能用栈上对象地址——子树生命周期不可控,一退出作用域就悬空。struct Node 至少得有 char op(运算符)、double val(数值)、Node* left 和 Node* right。运算符节点的 val 无意义,数值节点的 left/right 应为 nullptr。
常见错误:把 Node n{'+', 0, &l, &r} 这样局部变量取地址塞进树里,后续访问必崩溃。
建议直接写:
struct Node {
char op = 0;
double val = 0.0;
Node* left = nullptr;
Node* right = nullptr;
// 析构函数要递归 delete 子树
~Node() { delete left; delete right; }
};如何构建二元运算符节点
构造时顺序很重要:先建子树,再用它们初始化父节点指针。比如构建 a + b * c(右结合不考虑,按常规优先级),必须先建 * b c 子树,再以它为右子,a 为左子,建 + 节点。
立即学习“C++免费学习笔记(深入)”;
典型误操作:
- 先 new 一个
+节点,再临时赋left/right,但中间若抛异常,已分配的内存没被接管 → 内存泄漏 - 手动管理时忘记在拷贝/赋值中深拷贝,导致多个节点指向同一子树 →
delete两次崩溃
安全做法(裸指针):
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
Node* make_add(Node* l, Node* r) {
return new Node{'+', 0.0, l, r};
}
Node* make_num(double v) {
return new Node{0, v, nullptr, nullptr};
}
// 使用:
Node* tree = make_add(make_num(1.0), make_add(make_num(2.0), make_num(3.0))); // 1+(2+3)遍历求值时指针为空怎么防崩
所有对 left 或 right 的解引用前,必须显式判空。数值节点的左右子必然为空,运算符节点至少有一个非空(一元如负号可只用 left,但二元运算符要求两个都非空)。
容易漏掉的坑:
-
eval(nullptr)没检查,直接访问->op→ 段错误 - 递归调用
eval(node->left)前没确认node->left != nullptr,尤其在一元运算场景下
求值函数示例:
double eval(const Node* node) {
if (!node) return 0.0; // 防空
if (node->op == 0) return node->val; // 数值节点
double l = (node->left ? eval(node->left) : 0.0);
double r = (node->right ? eval(node->right) : 0.0);
switch (node->op) {
case '+': return l + r;
case '-': return l - r;
case '*': return l * r;
case '/': return r != 0 ? l / r : 0.0;
default: return 0.0;
}
}为什么别用 raw pointer 管理整棵树
裸指针本身不带所有权语义,Node* 可能只是观察者,也可能是唯一拥有者。一旦出现共享、复制、容器存储等需求,手动 delete 几乎必然出错。
实际项目中更稳妥的选择:
- 用
std::unique_ptr<node></node>替代Node*:移动语义天然支持树结构转移,析构自动递归释放,且禁止浅拷贝 - 若需共享子树(如公共子表达式优化),改用
std::shared_ptr<node></node>,但要注意循环引用风险(父子互相 hold) -
std::vector<:unique_ptr>></:unique_ptr>存储所有节点可避免 new/delete 配对失误,但需额外维护父子关系索引
裸指针只适合教学演示或极简嵌入式场景;只要涉及任意一点复杂控制流或异常路径,unique_ptr 就是底线。

















