一个有趣的删除和析构函数(C ++)

时间:2010-03-23 01:12:28

标签: c++ memory-management destructor

我有一段代码,我可以多次调用析构函数并访问成员函数,甚至在保留成员变量值的情况下调用析构函数。在调用delete之后,我仍然能够访问成员函数,但成员变量无效(全部为0)。我不能加倍delete。请解释一下。

#include <iostream>
using namespace std;

template <typename T>
void destroy(T* ptr)
{
    ptr->~T();
}

class Testing
{
public:
    Testing() : test(20)
    {

    }

    ~Testing()
    {
        printf("Testing is being killed!\n");
    }

    int getTest() const
    {
        return test;
    }

private:
    int test;
};

int main()
{
    Testing *t = new Testing();
    cout << "t->getTest() = " << t->getTest() << endl;

    destroy(t);
    cout << "t->getTest() = " << t->getTest() << endl;

    t->~Testing();
    cout << "t->getTest() = " << t->getTest() << endl;

    delete t;
    cout << "t->getTest() = " << t->getTest() << endl;

    destroy(t);
    cout << "t->getTest() = " << t->getTest() << endl;

    t->~Testing();
    cout << "t->getTest() = " << t->getTest() << endl;

    //delete t; // <======== Don't do it! Double free/delete!
    cout << "t->getTest() = " << t->getTest() << endl;

    return 0;
}

6 个答案:

答案 0 :(得分:16)

这是未定义行为的展示。通过一个已删除的指针调用成员函数,任何事情都会发生 - 编译器和运行时不需要检查这个错误,但你当然不能指望这个工作。

这与使用已释放的内存属于类似类别 - 您可能会在那里找到旧数据(或者您可能不会)。您可能会导致程序崩溃(或不崩溃)。您甚至可以在没有任何抱怨的情况下更改数据。

但无论如何,这是一个编程错误。

答案 1 :(得分:6)

仅仅因为您访问不再有效的对象并不意味着您的程序爆炸,这只意味着您的程序可以爆炸。

这是未定义的行为,这意味着任何事情都可能发生,甚至可能看起来做正确的事情。

答案 2 :(得分:5)

  

“一次调用析构函数   对象,对象不再存在;   如果是,行为是不确定的   为对象调用析构函数   其寿命已经结束“

C++ draft standard§12.4.12

正如其他人所指出的,这并不意味着实现总是会做一些明显不需要的事情(比如分段错误)。这意味着它可以做任何最方便的事情。

答案 3 :(得分:2)

您调用了未定义的行为,所有投注都已关闭。

答案 4 :(得分:2)

你真的不应该调用类的析构函数(除非你使用了placement new)然而回答你的问题,一旦删除了内存,访问指向该内存的指针会导致未定义的行为。在您的情况下,似乎您正在使用的内存已被释放以供将来使用,但它尚未被覆盖。所以你仍然能够访问它,但是不能保证何时该内存将被其他东西使用。

答案 5 :(得分:1)

正如在destroy()中那样,直接在main()中调用析构函数实际上并不会导致在C ++中销毁对象。只有此代码中的delete语句才能执行此操作。由于T的析构函数是良性的(它只是打印),这几乎没有效果。

由于没有成员函数是虚拟的,因此在销毁之后调用它们仍然会找到正确的代码来执行。在那里,this指针可能无效(在您调用delete之后),但这并不会阻止代码取消引用指针并返回int成员值的值