Destructor not working in C++ for anonymous object

2019-07-28 04:56发布

问题:

My friend told me C++ allows us to call a member function even if the instance is destroyed from memory. So I write the code below to verify it, but why the value of a can be extracted even after the object was destroyed? I thought there would be a segment fault.

#include <iostream>

class Foo{
public:
  Foo(int a = 0){
    std::cout << "created" << std::endl;
    this->a = a;
  }
  ~Foo(){
    std::cout << "destroyed" << std::endl;
  }
  Foo *f(){
    std::cout << "a=" << a << std::endl;
    return this;
  }
private:
  int a;
};

Foo *iwanttocallf(int i){
    return ((Foo)i).f();
}

int main(){
  for(int i = 0; i < 3; i++)
    iwanttocallf(i)->f();
}

Output from my Macbook Air:

created
a=0
destroyed
a=0
created
a=1
destroyed
a=1
created
a=2
destroyed
a=2

回答1:

Usually compilers are implementing the member function call as a call to a regular c function with the first argument a pointer to the object (gcc does it like that as far as I know). Now if your pointer is pointing to one destroyed object it doesn't mean that the memory where the a has been stored will be changed, it might be changed. So it is undefined behavior in general. In your case you got a value of a but maybe next time with a different compiler or different code you will crash. Try to use placement new operator then set a value of 'a' = 0 in destructor... and follow the memory where the object is stored.



回答2:

"My friend told me C++ allows us to call a member function even if the member is destroyed from memory"?

I don't know what your friend is trying to say. But you call member function on some object of a class unless it's a static member. So, if you delete that object from memory, how could you call any function of that class on that object. It's an undefined behavior.



回答3:

This is covered in §12.7 [class.cdtor]:

[..] For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.



回答4:

As other people have told, this involves undefined behavior, and any result is possible. I'll try to explain why you encountered this particular result (stuff working normally).

Objects in C++ are represented by contents of memory. When an object is destroyed, its destructor is executed, but the memory still contains the previous value. The output operation outputs the value taken from memory (which is now "free" - doesn't belong to any object) - if there is not much stuff going on between the destructor call and the output, the old value will remain.

However, if you change your code to add some calculations, the bug will be evident. For example, I added the following function that simulates some calculations:

int do_stuff()
{
    int result = 0;
    int x[3] = {0};
    for (auto& n: x)
    {
        n = rand();
        result ^= n;
    }
    return result;
}

I also added a call to this function:

  Foo *f(){
    std::cout << "foo1: a=" << a << std::endl;
    do_stuff();
    std::cout << "foo2: a=" << a << std::endl;
    return this;
  }

I got the output:

foo1: a=0
foo2: a=424238335

This clearly shows that it's not safe to expect anything consistent when dealing with deleted objects.

By the way, some debuggers overwrite the memory that deleted objects occupied with a special value like 0xcdcdcdcd - to make some sense out of this kind of unpredictable behavior. If you execute your code under such a debugger, you will see garbage printed, and will immediately know that your code is buggy.