是否值得在析构函数中设置NULL指针?

想象一下,我有一个分配内存的类(现在忘了关于智能指针):

class Foo { public: Foo() : bar(new Bar) { } ~Foo() { delete bar; } void doSomething() { bar->doSomething(); } private: Bar* bar; }; 

除了在析构函数中删除对象,还值得将它们设置为NULL吗?

我假设在上面的例子的析构函数中将指针设置为NULL是浪费时间。

由于析构函数是在“死亡”之前在对象上调用的最后一件事情,所以我认为之后不需要将其设置为NULL

在任何其他情况下,我总是设置一个指针为NULL后调用delete它。

有几个答案提到,在DEBUG构build中这样做可能是值得的,这有助于debugging。

不要这样做

您可能会帮助隐藏debugging版本中隐藏的问题,这些问题不会隐藏在您实际提供给客户的发布版本中(这与您的debugging版本应该具有相反的效果)。

如果你要“清除”dtor中的指针,一个不同的方式会更好 – 将指针设置为已知的错误指针值 。 那样的话,如果在最终尝试使用指针的地方存在对象的悬挂引用,那么将会得到可诊断的崩溃,而不是错误的代码,从而避免使用指针,因为它注意到它是NULL。

doSomething()看起来像:

 void doSomething() { if (bar) bar->doSomething(); } 

然后设置bar为NULL只是帮助隐藏一个错误,如果有一个悬挂引用一个被称为Foo::doSomething()的已删除的Foo对象。

如果你的指针清理如下:

 ~Foo() { delete bar; if (DEBUG) bar = (bar_type*)(long_ptr)(0xDEADBEEF); } 

你可能有更好的机会去捕捉这个bug(尽pipe只留下一小块可能会有类似的效果)。

现在,如果有任何对已被删除的Foo对象的悬挂引用,任何对bar使用都不会避免由于NULL检查而引用它 – 它会愉快地尝试使用指针,并且会得到一个可以修复的崩溃而不是在debugging版本中没有什么不好的情况发生,但是在你的客户的发布版本中,悬挂的引用仍然被使用(影响)。

在debugging模式下进行编译时,debugging堆pipe理器已经为您执行了这个任务(至lessMSVC的debugging运行时堆pipe理器将用0xDD覆盖释放的内存以指示内存已经死亡/已释放) 。

关键是如果您使用原始指针作为类成员,请不要在dtor运行时将指针设置为NULL。

这个规则也可能适用于其他原始指针,但这取决于指针的使用方式。

是的,这是浪费时间。

你不应该有两个原因:

  • 它有助于debugging,但在现代环境中,已删除的对象通常会在debugging版本中以可识别的位模式被覆盖。

  • 在大型应用程序中,可能会显着降低关机性能。 在最坏的情况下,closures你的应用程序意味着调用几十个不同的析构函数,写入数百个当前交换到磁盘的堆页。

首先,这是一个C实践,是一个有争议的实践。 有些人认为(对C来说)它隐藏了如果不被使用就会更快地出现的错误,并且不可能区分使用从未被分配过的内存部分和释放的内存部分。

现在在C ++? 这是没用的,但并不是因为与C相同的原因。

在C ++中,使用delete是一个错误。 如果你使用智能指针,你不会担心这一点,你不会冒险泄漏(即:你确定你的拷贝构造函数和指派操作符是否是exception安全的?线程安全的?)

最后,在析构函数中,它真的完全没用…一旦析构函数运行,访问对象的任何字段都是未定义的行为。 你已经释放了内存,所以完全可以写上其他的东西,覆盖你仔细放置的NULL。 实际上,内存分配器的工作方式通常是首先重新分配刚刚释放的区域:这样可以提高caching的性能……当然,如果我们谈论堆栈的话,这更加真实(哈哈!)。

我的意见: SAFE_DELETE是即将到来的厄运的标志。

由于debugging的原因,实际上可能是值得的。

通常情况下,不需要在析构函数中删除它们之后,将指针设置为NULL ,尽pipe它在debugging时可以方便地在检查类时指示资源是否已被正确释放。

一个常见的习惯用法是声明一个SAFE_DELETEmacros,它将删除指针并将其设置为NULL

 #define SAFE_DELETE(x) delete (x); x = NULL SAFE_DELETE(bar) 

这在指针稍后可以被重用的情况下特别有用。

国际海事组织它值得在DEBUG模式。 我经常发现它有帮助。 在RELEASE模式下,由于代码优化,通常会被编译器忽略,所以在生产代码中不应该依赖这个。

我会不惜一切代价避免原始指针,例如有和没有智能点明确地设置为NULL是有用的:

附:

  class foo { public: foo() : m_something( new int ) { } void doStuff() { // delete + new again - for whatever reason this might need doing m_something.reset( new int ); } private: std::unique_ptr<int> m_something; // int as an example, no need for it to be on the heap in "real" code } 

无:

 class foo { public: foo() : m_something( new int ) { } ~foo() { delete m_something; } void doStuff() { delete m_something; // Without this, if the next line throws then the dtor will do a double delete m_something = nullptr; m_something = new int; } private: int* m_something } 

不,这不值得。

但是如果你想保持一致,那么你应该可以这样做。

我通常会做的是创build一个免费的function,我可以使用每次我需要释放分配的数据(事件更多的免费function,如果需要的话)。 在这些函数中,build议将指针设置为NULL(或标识符为无效值)。

我认为这样做总是值得的(即使你没有技术上的需要)。 我设置了一个指向NULL的指针来指示它指向的内存不需要被释放。

另外如果在使用它之前检查指针是有效的。

 if (NULL == pSomething) { // Safe to operate on pSomething } else { // Not safe to operate on pSomething } 

在if条件中首先使用NULL,当你滑动时忽略pSomething为NULL,并错过第二个'='。 你得到一个编译错误,而不是一个需要时间追踪的错误。

删除它们后总是将指针设置为NULL是一个很好的做法。 还有一些代码检查工具强制执行此操作。