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? [复制]