An interesting case of delete and destructor (C++)

2019-02-19 16:05发布

问题:

I have a piece of code where I can call the destructor multiple times and access member functions even the destructor was called with member variables' values preserved. I was still able to access member functions after I called delete but the member variables were nullified (all to 0). And I can't double delete. Please kindly explain this.

#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;
}

回答1:

This is an exhibit of undefined behavior. Call a member function through a pointer that's been deleted and anything goes - the compiler and runtime aren't required to check for this error, but you certainly can't count on this working.

This falls into a similar category as using memory that's been freed - you might find your old data there (or you might not). You might cause the program to crash (or not). You might even be able to change the data without anything complaining.

But in any case, it's a programming error.



回答2:

Just because you are accessing a not longer valid object doesn't mean your program has to explode, it just means your program can explode.

It is undefined behavior, which means anything can happen, it might even appear to do the correct thing.



回答3:

"Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended"

C++ draft standard §12.4.12

As noted by others, this doesn't mean the implementation will always do something obviously unwanted (like a segmentation fault). It means it can do whatever is most convenient.



回答4:

You've invoked undefined behaviour, all bets are off.



回答5:

You really shouldn't call a class' destructor (unless you used placement new) however to answer your question, once memory has been deleted accessing pointers to that memory results in undefined behavior. In your case it appears as though the memory you are using has been freed for future use but it hasn't yet been overwritten. So you are still able to access it but there are no guarantees as to when that memory is going to be used by something else.



回答6:

Expressly calling the destructor, as you do in destroy(), and directly in main() doesn't actually cause an object to be destroyed in C++. Only the delete statement in this code does that. Since T's destructor is benign (it just prints), this has almost no effect.

Since none of the member functions are virtual, calling them after the destruction will still get to the right code to execute. Once there, the this pointer may be invalid (after your call to delete), but that doesn't stop the code from dereferencing the pointer and returning the value of the int member value.