在析构函数中将类成员设置为空

Setting class member to null in destructor

本文关键字:设置 成员 析构函数      更新时间:2023-10-16

本页有一段代码:

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] = '';
    }
    ~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,则使用已删除对象(例如通过指向该对象的悬空指针)的错误实际上可能被隐藏,因为有错误的代码可能会在尝试使用数据之前检查NULL。在一种思考方式中,这可能被认为是"安全的"行为,但是发生的事情是您实际上隐藏了一个已经发生的缺陷。

请记住,隐藏bug和修复bug是不一样的。在内存被重新分配之后,这个悬浮指针实际上可能会再次被使用,并将一个有效的指针放置在相同的内存位置。此时,有问题的代码将开始使用新的、有效的指针,但原因不正确。

因此,最好将指针设置为如果使用不当会导致崩溃的东西:

m_pchString = (char*) 0xdeaddead;

现在,如果有人试图使用已删除对象的成员指针(这是一个错误),它将很快失败,错误将被捕获而不是隐藏。

在使用MSVC(可能还有其他工具链)的调试构建中,您已经可以通过调试堆获得该行为。MSVC调试堆用值0xdd填充通过free()operator delete释放的内存:https://stackoverflow.com/a/370362/12711

除了Michael Burr提供的答案,在Tietbohl提供的链接的帮助下,这也是一个很好的答案。我引用它:

它可以帮助捕获许多对释放内存的引用(假设您的平台在null指针的转换上出错)。

它不会捕获所有对空闲内存的引用,例如,如果你有一个指针的副本。但有总比没有好。

它将掩盖双重删除,但我发现这些远不如访问已经释放的内存常见。

在很多情况下,编译器会把它优化掉。因此,不必要的论点不能说服我。

如果你已经在使用RAII,那么在你的代码中并没有太多的删除,所以额外的赋值导致混乱的论点并不能说服我。

在调试时,看到空值而不是过时的指针通常更方便。

如果这仍然困扰着你,使用智能指针或引用代替。

不设为NULL也无妨。对象删除后,无论如何都不应该访问它的成员数据。

好吧,当你已经删除了内存位置,析构函数只是一个很好的编程实践,它只是帮助在任何隐藏的错误存在