想象一下,我有一个分配内存的类(暂时忘记智能指针):
class Foo
{
public:
Foo() : bar(new Bar)
{
}
~Foo()
{
delete bar;
}
void doSomething()
{
bar->doSomething();
}
private:
Bar* bar;
};
Run Code Online (Sandbox Code Playgroud)
除了在析构函数中删除对象之外,还值得将它们设置为NULL吗?
我假设在上面示例的析构函数中将指针设置为NULL是浪费时间.
Mic*_*urr 33
几个答案提到在DEBUG构建中执行此操作以帮助调试可能是值得的.
不要这样做.
您可能只是潜在地帮助隐藏调试版本中的问题,该问题未在您实际提供给客户的版本构建中隐藏(这与调试版本应该具有相反的效果).
如果你要"清除"dtor中的指针,一个不同的习惯用法会更好 - 将指针设置为已知的坏指针值.这样,如果对某个最终尝试使用指针的对象有一个悬空引用,你将得到一个可诊断的崩溃而不是错误的代码,避免使用指针,因为它注意到它是NULL.
说doSomething()看起来像:
void doSomething()
{
if (bar) bar->doSomething();
}
Run Code Online (Sandbox Code Playgroud)
然后设置bar为NULL只是有助于隐藏一个错误,如果有一个悬挂的引用被Foo调用的已删除对象Foo::doSomething().
如果你的指针清理如下:
~Foo()
{
delete bar;
if (DEBUG) bar = (bar_type*)(long_ptr)(0xDEADBEEF);
}
Run Code Online (Sandbox Code Playgroud)
你可能有更好的机会捕获这个bug(虽然只是bar单独离开可能会产生类似的效果).
现在,如果有任何东西对已Foo被删除的对象有悬空引用,任何使用bar都不会因为NULL检查而无法引用它 - 它会很乐意尝试使用指针,你会得到一个崩溃,你可以修复而不是在调试版本中没有发生任何不好的事情,但悬空引用仍然在客户的发布版本中使用(生效).
当您在调试模式下进行编译时,调试堆管理器已经为您完成此操作的可能性非常大(至少MSVC的调试运行时堆管理器将使用0xDD覆盖释放的内存以指示内存已死/已释放) .
关键是如果你使用原始指针作为类成员,在dtor运行时不要将指针设置为NULL.
此规则也可能适用于其他原始指针,但这取决于指针的确切使用方式.
你不应该,原因有两个:
它有助于调试,但在现代环境中,删除的对象通常在调试版本中使用可识别的位模式进行覆盖.
在大型应用程序中,它可能会显着降低关闭性能.在最糟糕的情况下,关闭应用程序意味着调用许多不同的析构函数,并写入当前交换到磁盘的数百个堆页面.