C++ - 为啥在删除后将对象设置为空? [复制]

Posted

技术标签:

【中文标题】C++ - 为啥在删除后将对象设置为空? [复制]【英文标题】:C++ - Why set object to null after deleting? [duplicate]C++ - 为什么在删除后将对象设置为空? [复制] 【发布时间】:2013-01-03 05:04:15 【问题描述】:

我正在查看我在网上找到的以下链表代码:

void DeleteAfter(Node **head)
      if(*head==NULL)
            return;
      else
            Node *temp = NULL;
            temp = (*head)->next;
            (*head)->next = (*head)->next->next;
            delete temp;
            temp=NULL;
      

我对 C++ 不是很熟练,所以这可能是一个不好的问题,但是为什么 temp 被删除后被设置为 NULL 呢?这是必要的步骤吗?

【问题讨论】:

不要。只需使用智能指针。 这里是否设置为 NULL 完全无关紧要。 temp 是一个具有自动存储功能的变量,这意味着它会在退出 else 块后超出范围。但正如@chris 所说,只需使用智能指针 另外,*head 不是NULL 的事实并不意味着(*head)->next 不是NULL,您正在尝试取消引用该指针 ((*head)->next->...) 【参考方案1】:

没必要。有些人养成了这样做的习惯,即使没有结果。积极的编译器优化器将消除此代码,因此它实际上不会造成任何伤害。但我会写:

void DeleteAfter(Node *head) 
  if (head) 
    Node *next = head->next;
    if (next) 
      head->next = next->next;
      delete next;
    
  

注意我消除了一个无用的间接级别并添加了一个检查以确保有一个“节点之后”要删除。

这个习惯的基本原理是,如果一个指针总是指向一个有效的对象或者是空的,你可以依赖空检查等同于有效性检查。

出于这个原因,Ada(一种经常用于安全关键系统的语言)初始化指向 null 的指针并定义其 delete 等效运算符以自动将其参数设置为 null。您的 C++ 正在模拟这种行为。

在实践中,这门学科的价值并不是您所希望的。在很长一段时间内,它可以防止一个愚蠢的错误。然而,一件好事是,指针内容的调试器显示是有意义的。

【讨论】:

记住 head->next 可能为 NULL 导致这段代码在 head->next = head->next->next; 崩溃 如此真实...我更新以说明这一点。检查头节点而不检查head->next是没有意义的。【参考方案2】:

如果变量temp 以后可能会在代码中再次使用,那么最好将其设置为NULL。

通常在释放指针后将其设置为 NULL 有两个原因。

1.) 一旦你释放一个指针,你的程序就不能再使用指向地址的内存了。从理论上讲,该内存现在可以被任何其他程序使用,包括操作系统本身!试图释放一个已经被释放的指针,从而指向谁知道什么会导致一个巨大的问题。幸运的是,现代操作系统可以防止这种情况发生,但程序仍然会因非法访问错误而崩溃。释放一个空指针 OTOH 绝对不会做任何事情。

2.) 在使用 * 运算符取消引用指针之前,您应该始终检查指针不为 NULL。取消引用 NULL 指针将导致运行时错误。取消引用指向任意内存的已释放指针甚至更糟。释放的指针应始终设置为NULL,以便后面的代码可以假定非空指针指向有效数据。否则无法知道指针是否仍然有效。

至于最初的问题,指针变量temp 在一个不再使用的短函数中被声明为局部变量。在这种情况下,没有必要将其设置为 NULL,因为一旦函数返回,它就会超出范围。

但是,行...

(*head)->next = (*head)->next->next;

在尝试取消引用之前无法确保 (*head)->next 不为空,这是不行的。

更好的版本应该是……

int DeleteAfter(Node **head)
  Node *node_after = NULL;

  if(*head==NULL)
    return -1;

  node_after = (*head)->next;

  if(node_after == NULL)
    return -1;

  (*head)->next = node_after->next;
  delete node_after;

  return 0;
  

现在使用函数的人可以通过返回值检查节点删除是否成功,不存在尝试删除不存在节点的风险。

【讨论】:

【参考方案3】:

删除本地指针变量后不必将其设置为NULL。如果要重用指针,应将指针设置为NULL,在检查NULL后,您可以安全地为其分配一个新地址。通常我们这样做用于指针成员变量和全局指针变量。

【讨论】:

【参考方案4】:

如果 temp 是全局变量或成员变量,那么设置为 NULL 并不是一个坏主意。

在使用带有 C 代码的保守垃圾收集器后,我养成了将指针设置为 NULL 的习惯。没有指向未使用内存的指针是它找到要收集的垃圾的方式。但在这种情况下你也应该这样做

temp->next = NULL;

【讨论】:

【参考方案5】:

在您的代码示例中,没有明显的直接收益,但可以说是长期维护成本收益。这个想法是,在您删除 temp 后,有人最终可能会添加代码来尝试取消对 temp 的引用。这可能只是因为没有注意到删除,或者在删除后移动访问 temp 的较早行。

这是一个例子:

int * i = new int(12);
std::cout << *i << std::endl; // output is 12.
delete i;
// i = 0; // This would cause the program to fail on the next line.
std::cout << *i << std::endl; // output is random for me.

请注意,这不会隐藏缺陷,实际上不将指针设置为 null 会在这种情况下隐藏缺陷,因为 *i 返回一个随机值。

大多数人会说 i = 0 可能已被编译器优化,无论哪种方式,对指针的赋值都是无害的。对我来说,在专业发展时,我总是小心谨慎。

【讨论】:

【参考方案6】:

没有必要,有些人(包括我)认为这是不好的做法。

将其设置为NULL 的动机是您可以事后检查它是否被删除,如果没有则访问它。此外,这将防止双重删除,因为 NULL 指针上的 delete 是空操作。

另一方面,它可以隐藏错误。如果对象被删除了,那么使用它就没有意义了,对吧?您应该知道该对象已被删除,而不是依赖检查。

例如

if (p != NULL) //or just if (p)
   p->doStuff()

为什么?你不知道它是否被删除了吗?清理不是逻辑的一部分吗?

【讨论】:

以上是关于C++ - 为啥在删除后将对象设置为空? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 all() 为空的可迭代对象返回 True? [复制]

为啥 C++ 复制构造函数必须使用 const 对象?

为啥不同编译器使用 c++ string+string 临时对象有区别? [复制]

为啥迭代器指向的数据在从列表中删除后保留

移动语义只是一个浅拷贝并将其他指针设置为空吗?

为啥这是 C++ 中的有效函数声明? [复制]