想象一下,我有一个分配内存的类(暂时忘记智能指针):
class Foo
{
public:
Foo() : bar(new Bar)
{
}
~Foo()
{
delete bar;
}
void doSomething()
{
bar->doSomething();
}
private:
Bar* bar;
};
除了删除析构函数中的对象之外,还值得将它们设置为NULL吗?
我假设在上面示例的析构函数中将指针设置为NULL是浪费时间。
答案 0 :(得分:32)
有几个答案提到在DEBUG构建中执行此操作以帮助调试可能是值得的。
不要这样做。
您可能只是潜在地帮助隐藏调试版本中的问题,而这个问题并未隐藏在您实际提供给客户的版本构建中(这与您的调试版本应该具有相反的效果)。
如果你要'清除'dtor中的指针,一个不同的习惯用法会更好 - 将指针设置为已知的坏指针值。这样,如果对某个最终尝试使用指针的对象有一个悬空引用,你将得到一个可诊断的崩溃而不是错误的代码,避免使用指针,因为它注意到它是NULL。
说doSomething()
看起来像:
void doSomething()
{
if (bar) bar->doSomething();
}
如果对已调用bar
的已删除Foo
对象存在悬空引用,则将Foo::doSomething()
设置为NULL只会隐藏错误。
如果你的指针清理如下:
~Foo()
{
delete bar;
if (DEBUG) bar = (bar_type*)(long_ptr)(0xDEADBEEF);
}
你可能有更好的机会捕获这个错误(虽然只留下bar
可能会产生类似的效果。)
现在,如果有任何东西对已被删除的Foo
对象有悬空引用,则bar
的任何使用都不会避免由于NULL检查而引用它 - 它会愉快地尝试使用指针并且你会得到一个崩溃,你可以修复而不是在调试版本中发生任何不好的事情,但悬挂的引用仍然在客户的发布版本中使用(生效)。
当您在调试模式下进行编译时,调试堆管理器已经为您完成此操作的可能性非常大(至少MSVC的调试运行时堆管理器将使用0xDD覆盖释放的内存以指示内存已死/释放)。
关键是如果你使用原始指针作为类成员,在dtor运行时不要将指针设置为NULL。
此规则也可能适用于其他原始指针,但这取决于指针的确切使用方式。
答案 1 :(得分:27)
由于析构函数是对象在“死亡”之前调用的最后一件事,我会说之后不需要将它设置为NULL
。
在任何其他情况下,我总是在调用NULL
之后设置指向delete
的指针。
答案 2 :(得分:7)
是的,这是浪费时间。
答案 3 :(得分:5)
你不应该,原因有两个:
它有助于调试,但在现代环境中,已删除的对象通常在调试版本中使用可识别的位模式进行覆盖。
在大型应用程序中,它可能会显着降低关机性能。 在最糟糕的情况下,关闭应用程序意味着调用许多不同的析构函数,并写入当前交换到磁盘的数百个堆页面。
答案 4 :(得分:4)
首先,这是一个C练习,也是一个有争议的练习。有些人认为(对于C)它隐藏了如果没有使用它会更快浮出水面的错误,并且不可能区分释放的内存部分的使用与使用从未分配的错误...
现在用C ++?它没用,但不是出于与C相同的原因。
在C ++中,使用delete
是一个错误。如果您使用智能指针,您不会担心这一点,并且您不会冒泄漏的风险(即:您确定您的复制构造函数和分配运算符是异常安全的吗?线程安全吗?)
最后,在析构函数中它确实是完全无用的......一旦析构函数运行,访问对象的任何字段都是未定义的行为。你已经释放了内存,所以完全可以在那里写一些东西,覆盖你精心设置的NULL。实际上,内存分配器的工作方式通常是首先重新分配新释放的区域:这会增强缓存性能......如果我们正在讨论堆栈,那么这甚至更真实(哈哈!)。
我的观点:SAFE_DELETE
表示即将到来的厄运。
答案 5 :(得分:2)
出于调试原因,实际上可能值得。
答案 6 :(得分:1)
通常,不,在析构函数中删除它们之后不需要显式设置指针{ - 1}}虽然在检查类时指示资源是否已正确释放,但在调试期间它可以是一个方便的帮助或不。
一个常见的习惯用法是声明一个NULL
宏,它会删除指针并为你设置SAFE_DELETE
:
NULL
这在以后可能重用指针的情况下特别有用。
答案 7 :(得分:1)
IMO在DEBUG模式下值得。我觉得它经常有用。在RELEASE模式下,由于代码优化,编译器通常会跳过它,因此您不应该在生产代码中真正依赖它。
答案 8 :(得分:1)
我会不惜一切代价避免使用原始指针,例如有和没有智能点,明确地将其设置为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
}
答案 9 :(得分:0)
不,这不值得。
但是如果你想保持一致,那么你应该这样做。
我通常会创建一个 Free 函数,每次我需要释放分配的数据时都可以使用它(如果需要,可以使用更多自由函数)。在这些函数中,建议将指针设置为NULL(或将标识符设置为无效值)。
答案 10 :(得分:0)
我认为总是值得这样做(即使你在技术上并不需要)。我将指针设置为NULL以指示它指向的内存不需要解除分配。
此外,如果在使用之前检查指针是否有用,也是有用的。
if (NULL == pSomething)
{
// Safe to operate on pSomething
}
else
{
// Not safe to operate on pSomething
}
在if条件中先置NULL,防止在滑动时将pSomething设置为NULL并错过第二个'='。您会收到编译错误,而不是需要时间跟踪的错误。
答案 11 :(得分:-1)
最好在删除指针后始终将指针设置为NULL。还有一些代码检查工具强制执行此操作。