c++:`this == nullptr` 在成员函数中安全吗?

c++: Is `this == nullptr` safe in member functions?

计算二叉树的高度,使用height(left)+height(right)+1,不存在节点的高度定义为-1。因此,如果 left 或 right 为 null,则公式无需明确指定这些情况即可工作。

我在 C++ 中实现了这个想法,并且我确保成员函数不会访问成员变量 this == nullptr。我的实施是成功的并且有效(至少在我的 Linux 机器上)。

我想知道这种方法可能有什么样的实际缺点,以及标准是否对此有任何规定。那么,这就是我的问题。

#include <algorithm>
#include <iostream>

struct Node {
    // default values of the child nodes are NULL
    Node *left = nullptr;
    Node *right = nullptr;

    // store some value
    int v;

    int get_height() const __attribute__ ((optimize(0))) {
        if (this == nullptr)
            return -1;
        return std::max(left->get_height(), right->get_height()) + 1;
    }
};

int main() {
    Node node1, node2, node3, node4;
    node2.left = &node1;
    node2.right = &node3;
    node3.right = &node4;
    std::cout << node2.get_height() << std::endl;
    return 0;
}

编辑:如果启用了优化,代码将因分段错误而失败。可以通过将 __attribute__ ((optimize(0))) 附加到函数来解决问题。

this == nullptr 是“安全的”,因为它没有任何副作用。

this == nullptr 无用的 ,因为在任何具有明确行为的程序中,它永远不会 true。因此,允许优化器假装你写了:

if (false)
    return -1;
return std::max(left->get_height(), right->get_height()) + 1;

这与写入相同:

return std::max(left->get_height(), right->get_height()) + 1;

通过空指针(或任何其他不指向具有活动生命周期的对象的指针)调用成员函数是不安全的。这种检查并不能防止这种情况发生,尽管人们可能凭直觉认为它可以。示例程序的行为未定义。

定义明确的函数实现示例:

int get_height() const {
    int lh = left  ? left->get_height()  : -1;
    int rh = right ? right->get_height() : -1;
    return std::max(lh, rh) + 1;
}

非成员函数可能有助于避免一点点重复。


P.S。一个好的编译器会警告你潜在的错误:

warning: 'this' pointer cannot be null in well-defined C++ code; comparison may be assumed to always evaluate to false [-Wtautological-undefined-compare]

    if (this == nullptr)
        ^~~~    ~~~~~~~

不,if (this == nullptr) 不是 "safe"。 if 语句可能为真的唯一方法是如果您在空指针上调用成员函数并且这样做是未定义的行为。这意味着检查是完全多余的,因为它在法律代码中永远不会是真的。

this 不能是 nullptr 并且编译器可能会优化它,因为 this 是空指针是 UB 的结果。

相似案例:

  ptr->someStaticMethod();   // if ptr is null this is an UB, even if method is static.

  if(!ptr) 
      abort();   // May never happen, even if ptr is nullptr

如果您希望在尝试获取对象的高度时能够处理 nullptr,您应该使用非成员函数。

namespace MyApp
{
   int get_height(Node const* n)
   {
      return ( n == nullptr ) ? -1 : n->get_height();
   }
}

并使用

Node* n = ...;

...

int h = MyApp::get_height(n);