当我在C ++类中包装“原始”资源时,在析构函数代码中我通常只是释放已分配的资源,而不关注其他步骤,如清零指针等。 e.g:
class File
{
public:
...
~File()
{
if (m_file != NULL)
fclose(m_file);
}
private:
FILE * m_file;
};
我想知道这段代码样式是否包含潜在错误:即是否可能多次调用析构函数?在这种情况下,正确的做法是在析构函数中将清除指针以避免双重/多重破坏:
~File()
{
if (m_file != NULL)
{
fclose(m_file);
m_file = NULL; // avoid double destruction
}
}
可以对堆分配的内存进行类似的示例:如果m_ptr
是指向分配有new[]
的内存的指针,那么下面的析构函数代码是否正常?
// In destructor:
delete [] m_ptr;
还是指针也应该被清除,以避免双重破坏?
// In destructor:
delete [] m_ptr;
m_ptr = NULL; // avoid double destruction
答案 0 :(得分:5)
没有。如果你有Close()
函数之类的东西很有用:
void Close()
{
if (m_file != NULL)
{
fclose(m_file);
m_file = NULL;
}
}
~File()
{
Close();
}
这样,Close()
函数是幂等的(您可以根据需要多次调用它),并且在析构函数中避免一次额外的测试。
但是由于C ++中的析构函数只能被调用一次,所以为指针指定NULL是没有意义的。
当然,除非出于调试目的,否则,特别是如果您怀疑是双重删除。
答案 1 :(得分:2)
如果多次调用析构函数,则您已经有未定义的行为。这也不会影响可能具有指向资源本身的指针的客户端,因此这不会阻止双重删除。 unique_ptr
或scoped_ptr
对我来说似乎是更好的解决方案。
答案 2 :(得分:2)
在一个有缺陷的应用程序中(例如,不正确地使用std :: unique_ptr<>可能导致两个std :: unique_ptr<>持有相同的原始指针),你最终可能会被双重删除,因为第二个超出范围。
我们关心这些不良案例 - 否则,讨论在析构函数中设置指向nullptr
的指针有什么意义?无论如何它都会消失!
因此,在这个例子中,至少,在单元测试期间让程序在一个调试器中出现seg-fault会更好,因此你可以追踪问题的真正原因。
因此,一般来说,我发现设置指向nullptr
的指针对内存管理特别有用。
您可以这样做,但更强大的选择是进行单元测试并明智地使用内存检查器,例如valgrind。< / em>的
毕竟,由于存在一些内存错误,你的程序似乎可以运行很多次,直到它意外崩溃 - 使用内存检查器进行质量保证更加安全,特别是当程序变大时,内存错误变得更加容易不太明显。