删除指针后将其设置为NULL是一个好的做法吗?

16 浏览
0 Comments

删除指针后将其设置为NULL是一个好的做法吗?

我先说一下,使用智能指针,你就不必担心这个问题了。

以下代码存在哪些问题?

Foo * p = new Foo;
// (使用 p)
delete p;
p = NULL;

这是受到另一个问题的答案和评论的启发。来自Neil Butterworth的一条评论获得了一些赞同票:

在C++中,将指针设置为NULL并不是普遍的良好做法。有时这样做是有好处的,有时则是没有意义的,甚至可能隐藏错误。

有很多情况下它并不会有所帮助。但根据我的经验,它无害。有人能给我解释一下吗?

0
0 Comments

在使用指针的时候,将指针设置为NULL并没有什么坏处,但这通常只是对一个更根本的问题的临时解决方法。为什么首先要使用指针呢?有两个典型的原因:

- 你只是想在堆上分配一些内存。在这种情况下,将其包装在一个RAII对象中会更安全和更清晰。当你不再需要对象时,结束RAII对象的作用域。这就是std::vector的工作原理,它解决了意外留下指向已释放内存的指针的问题。这里没有指针。

- 或者你可能想要一些复杂的共享所有权语义。从new返回的指针可能与调用delete的指针不同。在此期间,多个对象可能同时使用该对象。在这种情况下,最好使用共享指针或类似的东西。

我的经验法则是,如果你在用户代码中留下指针,那么你做错了。指针不应该指向垃圾。为什么没有一个对象来负责确保其有效性?为什么它的作用域没有在指向的对象结束时结束?

所以你的观点是,首先不应该有原始指针,任何涉及到该指针的东西都不应该被称为“良好的实践”?说得没错。

嗯,或多或少。我不会说没有涉及原始指针的任何东西都可以称为良好的实践。只是这是个例外而不是规则。通常,指针的存在表明在更深层次上有问题。

但是,为了回答这个直接的问题,不,我不明白将指针设置为NULL如何会导致错误。

你在分条部分之后的第一段让我点赞。如果你的功能有问题,无论你是否将其设置为null,大部分指针问题都会消失。

我不同意——有些情况下使用指针是有好处的。例如,堆栈上有两个变量,你想要选择其中一个。或者你想要将一个可选的变量传递给一个函数。我会说,你不应该在使用new时使用原始指针。

没错,但是Boost并不总是一个选择,有时候使用指针的简单性可能更可取。(每个人都知道指针可以指向一个对象或null。人们可能不熟悉boost::optional)

我完全不同意将它们设置为NULL是一个临时措施。这实际上是一种捕捉错误的有效方法。每当可能时,使用RAII包装器肯定是最好的选择,但有时你必须实现自己的包装器,在这种情况下,这是一个有效的问题。

你所说的“你自己的包装器”是什么意思?为什么不使用RAII来创建包装器?它是一种临时措施,因为它修复的错误只是因为你没有修复“根本”问题(变量仍然在作用域内)。

有时可能没有预先制作的适用于所需资源类型的包装器类,这种情况下你必须编写自己的。实现将需要一个指针(或其他资源“句柄”)。

是的,但是实现不会将该指针暴露在其所在的作用域之外。如果指针是RAII包装器的私有成员,它将在包装器结束时结束其作用域,所以没有什么可以设置为NULL的。

那又怎样?实现可能仍然需要处理该指针,所以它应该将指针设置为NULL吗?我的答案是肯定的。

当一个指针超出作用域时,我不明白为什么任何人或任何东西可能需要处理它。

“我不明白将指针设置为null如何会导致错误”——如果这样做会导致错误,那么代码本来就是有问题的,因为delete操作符对非const的lvalue操作数是允许修改其值的,特别是允许将其设置为null指针。所以如果将其设置为null导致错误,那么原始代码就是多余的非可移植的。我不知道任何实现会在delete时清空指针,因此只能说是“有问题的”。

如果有两个局部变量,你想要选择一个,正确的工具是引用,而不是指针。除了增加的安全性外,还有可能不必将变量放在堆栈上,而可以将其保留在寄存器中。获取某个东西的地址会强制编译器将该东西放在内存中,而它可能本来不需要放在内存中。

0
0 Comments

在C++中,将指针设置为0(标准C++中的“null”,C中的NULL定义有些不同)可以避免双重删除导致的崩溃。

考虑以下情况:

Foo* foo = 0; // 将指针设置为0(C++中的NULL)
delete foo; // 不会执行任何操作

而:

Foo* foo = new Foo();
delete foo; // 删除对象
delete foo; // 未定义的行为

换句话说,如果不将删除的指针设置为0,如果进行了双重删除,就会遇到问题。反对在删除后将指针设置为0的观点是,这样做只是掩盖了双重删除错误并将其置之不理。

显然,最好不要有双重删除错误,但根据所有权语义和对象生命周期的不同,在实践中可能很难实现。与UB相比,我更喜欢掩盖的双重删除错误。

最后,关于对象分配管理的一点提示,建议您查看std::unique_ptr用于严格/独占所有权,std::shared_ptr用于共享所有权,或者根据需要选择另一个智能指针实现。

您的应用程序并不总是在双重删除时崩溃。根据两个删除之间发生的情况,任何事情都可能发生。最有可能的是,您将破坏堆,并在以后的完全不相关的代码中崩溃。虽然段错误通常比静默忽略错误更好,但在这种情况下不能保证发生段错误,并且其效用值得怀疑。

问题在于存在双重删除。将指针设置为NULL只是隐藏了这个事实,而不是修复或使其更安全。想象一下,维护人员一年后回来看到foo已被删除。他现在相信可以重新使用指针,不幸的是,他可能错过了第二次删除(它甚至可能不在同一个函数中),现在指针的重新使用将被第二次删除破坏。第二次删除后的任何访问都是一个严重的问题。

确实,将指针设置为NULL可以掩盖双重删除错误。(有些人可能认为这是一个解决方案-确实是,但不是一个很好的解决方案,因为它没有解决问题的根本原因。)但是不将其设置为NULL掩盖了更常见的问题,即在删除后访问数据。

据我所知,std::auto_ptr在即将发布的C++标准中已被弃用。

我不会说它被弃用了,这听起来好像这个想法已经消失了。相反,它正在被unique_ptr取代,它使用了移动语义来实现auto_ptr尝试实现的功能。

说得好,“被取代”才是正确的说法;-)

0
0 Comments

在这段对话中,讨论了在删除指针后是否将其置为NULL的问题。以下是问题出现的原因和解决方法的总结:

问题出现的原因:

- 删除指针后,指针仍然指向之前指向的内存地址,这可能导致悬空指针问题,即在程序中继续使用已经被删除的指针。

- 悬空指针问题可能导致程序崩溃或产生不可预料的行为。

解决方法:

- 在删除指针后,将指针置为NULL(或nullptr),以避免悬空指针问题的发生。

- 可以使用作用域来管理指针的生命周期,在作用域结束时自动删除指针并将其置为NULL。

- 可以使用RAII(资源获取即初始化)的设计模式,将指针封装在类中,在类的析构函数中删除指针并将其置为NULL。

- 可以使用STL(标准模板库)等现成的内存管理工具,避免手动处理内存。

例子代码:

{
    Foo* pFoo = new Foo;
    // 使用pFoo
    delete pFoo;
    pFoo = nullptr; // 或 pFoo = NULL;
}

在讨论中,人们提出了不同的观点。有人认为使用作用域来管理指针的生命周期是最佳实践,可以在作用域结束时自动删除指针并将其置为NULL。有人认为RAII设计模式更简单,也可以避免悬空指针问题。也某些情况下可以使用现成的内存管理工具,如STL,来避免手动处理内存。同时,也有人对将指针置为NULL的做法提出质疑,认为这种做法可能会导致代码变得混乱,并且并不能解决指针问题的根本原因。

将指针置为NULL是一种常见的防止悬空指针问题的做法,但并不是唯一的解决方案。根据具体情况选择合适的内存管理方式是更重要的。

0