在this页面中有一段代码:
class MyString
{
private:
char *m_pchString;
int m_nLength;
public:
MyString(const char *pchString="")
{
// Find the length of the string
// Plus one character for a terminator
m_nLength = strlen(pchString) + 1;
// Allocate a buffer equal to this length
m_pchString = new char[m_nLength];
// Copy the parameter into our internal buffer
strncpy(m_pchString, pchString, m_nLength);
// Make sure the string is terminated
m_pchString[m_nLength-1] = '\0';
}
~MyString() // destructor
{
// We need to deallocate our buffer
delete[] m_pchString;
// Set m_pchString to null just in case
m_pchString = 0;
}
char* GetString() { return m_pchString; }
int GetLength() { return m_nLength; }
};
在析构函数中,编写器将 m_pchString 设置为null,以防万一。如果我们不将它设置为null,会发生什么?我们已经释放了指定的内存,类成员将在退出时被杀死。这样做有什么好处?
答案 0 :(得分:1)
如果指针设置为NULL
,则可能实际上隐藏了使用已删除对象的错误,例如通过指向对象的悬空指针,因为错误代码可能会检查NULL
在尝试使用数据之前。在一种可能被认为是安全的思维方式中。行为,但发生的事情是你实际上隐藏了已经发生的缺陷。
请记住,隐藏错误与修复错误不同。在重新分配内存后,实际上可以再次使用该悬空指针,并将有效指针放在同一个内存位置。此时,有缺陷的代码将开始使用新的有效指针,但原因不正确。
因此,实际上将指针设置为可能导致崩溃的事情可能会更好,如果它使用不当:
m_pchString = (char*) 0xdeaddead;
现在,如果某些东西试图使用已删除对象的成员指针(这是一个错误),它将快速失败,并且将捕获该错误而不是隐藏错误。
在调试版本中使用MSVC(以及可能的其他工具链),您已经可以通过调试堆获得该行为。 MSVC调试堆填充通过free()
或operator delete
释放的内存,值为0xdd:https://stackoverflow.com/a/370362/12711
答案 1 :(得分:1)
除了Michael Burr提供的答案之外,在Tietbohl提供的链接的帮助下,this也是一个很好的答案。我引用它:
它可以帮助捕获许多对自由内存的引用(假设你的平台在空指针的deref上出错)。
它不会捕获所有对自由内存的引用,例如,如果你有一个指针的副本。但有些人比没有人好。
它会掩盖双重删除,但我发现这些远比访问已释放的内存少得多。
在许多情况下,编译器会优化它。因此,不必要的论点并没有说服我。
如果你已经在使用RAII,那么你的代码中就不会有很多删除事件,因此额外分配导致混乱的论点并不能说服我。
在调试时,通常很方便查看空值而不是过时的指针。
如果这仍然困扰你,请使用智能指针或参考。
答案 2 :(得分:0)
如果不将其设置为NULL,则没有任何害处。删除对象后,您无论如何都不应该访问其成员数据。
答案 3 :(得分:0)
当你已经删除了内存位置时,析构函数只是一个很好的编程实践,只有在出现任何隐藏错误的情况下才有帮助。