我想首先需要回答的问题是为什么善意的人们会写支票。
最常见的情况可能是您的类是自然发生的递归调用的一部分。
如果你有:
struct Node
{
Node* left;
Node* right;
};
在 C 语言中,你可以这样写:
void traverse_in_order(Node* n) {
if(!n) return;
traverse_in_order(n->left);
process(n);
traverse_in_order(n->right);
}
在 C++ 中,最好将其设为成员函数:
void Node::traverse_in_order() {
// <--- What check should be put here?
left->traverse_in_order();
process();
right->traverse_in_order();
}
在 C++ 的早期(标准化之前),人们强调成员函数是函数的语法糖,其中this
参数是隐式的。代码是用 C++ 编写的,转换为等效的 C 语言并编译。甚至还有明确的例子来比较this
to null 是有意义的,最初的 Cfront 编译器也利用了这一点。因此,对于具有 C 语言背景的人来说,检查的明显选择是:
if(this == nullptr) return;
注:Bjarne Stroustrup 甚至提到规则this
这些年来已经发生了变化here
这在许多编译器上运行了很多年。当标准化发生后,情况发生了变化。最近,编译器开始利用调用成员函数,其中this
being nullptr
是未定义的行为,这意味着该条件始终是false
,编译器可以随意省略它。
这意味着要遍历这棵树,您需要:
-
致电前完成所有检查traverse_in_order
void Node::traverse_in_order() {
if(left) left->traverse_in_order();
process();
if(right) right->traverse_in_order();
}
这意味着还要检查每个调用站点是否可能有空根。
-
不要使用成员函数
这意味着您正在编写旧的 C 风格代码(可能作为静态方法),并使用对象作为参数显式调用它。例如。你又开始写作了Node::traverse_in_order(node);
而不是node->traverse_in_order();
在呼叫站点。
-
我相信以符合标准的方式修复这个特定示例的最简单/最巧妙的方法是实际使用哨兵节点而不是nullptr
.
// static class, or global variable
Node sentinel;
void Node::traverse_in_order() {
if(this == &sentinel) return;
...
}
前两个选项似乎都不吸引人,虽然代码可以逃脱惩罚,但他们用this == nullptr
而不是使用正确的修复方法。
我猜这就是其中一些代码库演变为具有this == nullptr
检查他们。