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

Vie*_*iet 2 c++ destructor memory-management

我有一段代码,我可以多次调用析构函数并访问成员函数,甚至在保留成员变量值的情况下调用析构函数.在我调用之后,我仍然能够访问成员函数,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;
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 16

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

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

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

  • 在你做的时候明确地调用析构函数不涉及释放内存.它仍然是一个编程错误,只是没有抓住它.释放内存两次(通过调用delete)两次可能导致堆管理器崩溃或以其他方式抱怨.请注意,虽然允许显式调用析构函数,但通常不会这样做,因为析构函数将通过`delete`调用,或者当对象超出范围(或其他RAII场景)时调用.我唯一一次看到明确调用dtor的有效实例是使用放置`new`. (3认同)

sth*_*sth 6

仅仅因为您正在访问不再有效的对象并不意味着您的程序必须爆炸,这只意味着您的程序可能会爆炸.

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

  • @greyfade - 不,恶魔*可能*从你的鼻子飞出来.无论如何都无法保证. (2认同)

Mat*_*hen 5

"一旦为对象调用析构函数,该对象就不再存在;如果为生命周期已结束的对象调用析构函数,则行为未定义"

C++草案标准 §12.4.12

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