【问题标题】:Should I check for NULL in my assignment operator?我应该在赋值运算符中检查 NULL 吗?
【发布时间】:2011-10-07 23:20:07
【问题描述】:

我有课

class Node {

  public:
    int value;
    Node * next;

    Node();
    Node(const Node& other);
    Node& operator= (const Node& other);
};

它不是很有用,但它有一个重写的赋值运算符。里面的所有东西都是public,因为我是一个非常开放和合作的人。

现在我在其他地方有一个这些节点的数组:

Node * nodes = new Node[15];

当我尝试将一个节点分配给我的节点数组时:

nodes[0] = Node();

我遇到了严重的崩溃。

我的赋值运算符如下所示:

Node& Node::operator= (const Node& other) {

  // watch out for self assignment
  if (this == &other) return *this;

  delete this->next;
  this->next = new Node(*(other.next)); // call the copy constructor
  this->value = other.value;

  return *this;
}

我觉得在尝试取消引用其成员之前,我应该检查 this 是否为 NULL。对可能出了什么问题有任何想法吗?

【问题讨论】:

  • *(other.node) 不应编译。 Node 没有成员 node
  • 后编辑:delete this->node 也不应该编译。 Node 没有成员 node。也许你还没有发布真正的代码?
  • 啊哈哈,是的。谢谢。不,这不是真正的代码,这足以表达情况。
  • 另外,您的“删除 + 分配新”模式不是异常安全的。
  • @asveikau 为什么“删除+分配新”不是异常安全的?听起来很有趣,我很想知道!

标签: c++ assignment-operator


【解决方案1】:

你永远不应该检查thisNULL;在有效对象上调用非静态成员函数是非法的。

您可能必须确保分配中源对象和目标对象的next 指针成员变量为空或指向有效对象。如果没有看到真实的代码,就不可能说你现在拥有的构造函数是否正确地做到了这一点。

【讨论】:

    【解决方案2】:

    问题是你取消引用other.next,它可能是NULL。所以你应该在取消引用之前检查other.next 是否为空:

    this->next = other.next ? new Node(*other.next) : 0;
    

    取消对 NULL 指针的引用是未定义的,因此如果您这样做,任何事情都可能发生——它可能不会立即崩溃,而是会跑到意想不到的地方,让您和调试器都感到困惑。

    【讨论】:

    • 取消引用空指针是未定义的,但肯定会触发分段错误。另一方面,取消引用 未初始化的 指针可能会产生奇怪的结果。
    • @okorz001:这两个操作都会导致未定义的行为,除了奇怪的结果之外,我没有任何理由期待任何其他结果。
    • @okorz001 取决于您的架构和用户空间/内核空间,但您可以使用 NULL 指针。
    • 比我的回答还要好。直奔问题。
    【解决方案3】:

    我加倍说你不应该检查这个是否为 NULL。 我相信您的所有权在此处设置不明确。谁拥有下一个节点?

    This ->next 或 other ->next 可能为 null,因此请在删除/取消引用之前检查。

    如果不了解您的代码的更多信息,就很难说出您想要实现的目标。

    【讨论】:

      【解决方案4】:

      this 永远不会是 NULL,但是在您的示例中,this->next 将是 NULL 或 nodes[0] 的无效引用,因为该对象从未被初始化(我不太确定编译器做了什么,也许它用零填充数组,也许不是)。我建议你提防delete this->next; 使用NULL 指针,并确保你的数组在分配后是空白的。 IE。进行以下更改:

      Node * nodes = new Node[15] {0};

      if (this->next) delete this->next;

      【讨论】:

      • -1: IMO 这是个坏建议——确保next 未被初始化的正确方法是在所有节点构造函数中初始化它。在调用 delete 之前检查指针是否为 null 同样没有意义——删除 null 指针是一个定义明确的 noop
      • 关于删除 null 是安全的公平点(尽管说删除空指针是一个 noop 并保证是安全的,但这种假设可能并不总是正确的,也许某些实现是故意愚蠢的)。然而,在这种情况下,nodes[0] 处的 Node 对象没有被任何构造函数初始化,因为它来自未初始化的数组分配。我假设这个数组包含不确定的数据,导致 delete this->next 崩溃。
      • 节点的默认ctor是通过更新数组来调用的。 next 的初始化程序是必须的。未初始化的指针是通往地狱的某种方式!
      • 我们还没有看到默认的ctor,但它似乎忽略了初始化下一个指针。我可以看到两个选项:将默认 ctor 更改为下一个初始化,或者按照我的答案将数组初始化为零。我不认为后者是更好的选择,但它是修复崩溃的一种方法。因此,与其对我投反对票,不如对其他答案投赞成票。
      • 零初始化不起作用,因为没有接受 int 的 Node ctor。即使你添加了这样一个 Node(int) ctor,你仍然需要初始化那个 ctor 中的 next 指针,否则它不会被初始化。尽管如此,尝试使用您的建议只会导致编译时错误。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-27
      • 2011-10-18
      • 1970-01-01
      • 2017-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多