using an object after it's destructor is calle

2019-08-11 01:38发布

问题:

Possible Duplicate:
Can a local variable’s memory be accessed outside its scope?

Code:

#include <iostream>
using namespace std;

class B{
    public:
    int b;
    B():b(1){}
    ~B(){cout << "Destructor ~B() " << endl;}
};

class A{
    public:
    B ob;
    A()try{throw 4;}
    catch(...){cout << "Catched in A() handler : ob.b= " << ob.b<<endl;}
};




int main()try{
A t;


}
catch(...){cout << "CATCHED in Main" <<  endl;}

Output:

Destructor ~B() 
Catched in A() handler : ob.b= 1
CATCHED in Main

My Question is how it's possible to access a member variable b of an object ob that its destructor call finished.

回答1:

Because while the object has been destroyed, the actual memory that it occupied still exists. However, it's undefined behavior, and may or may not work.



回答2:

Using a destructed object is undefined behaviour. This means that you may be getting this behaviour now, but nothing guarantees that you will get it some other time. Undefined behaviour is more dangerous than a regular bug because it can be more difficult to detect, as this example shows.

UPDATE: Following some comments, I'll explain why OP's code produces that output.

try function blocks are a somewhat obscure feature in C++. You can surround the whole body of a function inside a try block, with its corresponding catch. This is, instead of:

void foo()
{
  try
  {
    //...
  }
  catch (/*whatever*/)
  {
    //...
  }
}

You can write:

void foo()
try
{
    //...
}
catch (/*whatever*/)
{
  //...
}

This is not too useful, really, but can be marginally useful for constructors, because this is the only way to include the initialisation list inside the try block. Thus, OP's code for the A constructor is equivalent to:

A()
try
: b()
{
  throw 4;
}
catch(...)
{
  cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
}

I said this is just marginally useful because you cannot use the object you are constructing inside the catch block; if you throw inside the try block, the exception will leave the constructor body, so the object will have never been constructed and any constructed data member will be immediately destructed before entering the catch block. But it might have some use for logging purposes.

Now, as we all know, a constructor (asuming we are not using the nothrow version) can only do two things:

  1. Return a constructed object
  2. Throw an exception

In this constructor we throw, but the exception is caught in the catch block. So what happens now? What will be returned to the code calling the constructor? We cannot return a constructed object because we have none, so there is only one alternative: the catch block must throw. And this is actually what the standard mandates in this case. If we do not throw explicitly, the compiler will silently add a throw; instruction at the end of the catch block. So, elaborating a bit more, the constructor is equivalent to the following:

A()
try
: b()
{
  throw 4;
}
catch(...)
{
  cout << "Catched in A() handler : ob.b= " << ob.b<<endl;
  throw;
}

And this is the reason why the exception is caught twice: once in A constructor and once in main().



回答3:

That might be a bug in your compiler.

When I ran your code, the destructor is called in the proper order. The output is:

Catched in A() handler : ob.b= 1
Destructor ~B()

And I can't imagine any reason why catch in main is executed. The exception was already caught in A::A().

Update

I was confused by the edit. Originally, it was initializer list try/catch syntax which makes a difference. Now I can reproduce OP's output and it is UB indeed. I got a crash.