2

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.

Community
  • 1
  • 1
AlexDan
  • 3,203
  • 7
  • 30
  • 46
  • 3
    This is not the same question, but Eric's answer on it is still applicable: http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope/6445794#6445794 – R. Martinho Fernandes Oct 25 '12 at 11:44
  • @R.MartinhoFernandes: Ah, one of SO all-time classics :) Well brought. – Gorpik Oct 25 '12 at 11:55

3 Answers3

8

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().

Gorpik
  • 10,940
  • 4
  • 36
  • 56
3

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • you said that the actual memory that it occupied still exists. does it mean that this memory will still exist until the "ob" object of type "B" deallocated, which is when the "t" object of type "A" get deallocated ? – AlexDan Oct 25 '12 at 11:47
  • 3
    @AlexDan: The object of type `A` has never existed, since it was never fully constructed, and the `B` object memory has already been deallocated. The memory has been released, but its contents are still there because it has not been reused yet. Nevertheless, you cannot rely on that; nothing guarantees that the runtime will not decide to reuse this memory in a future run of the code (probably in front of your customer, of course). – Gorpik Oct 25 '12 at 11:52
2

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.

Andriy
  • 8,486
  • 3
  • 27
  • 51
  • In fact, it is your compiler that is not conforming to the standard. That destructor should be called as soon as the `try` block is left. Anyway, since we are running into undefined behaviour here, calling the destructor after the `catch` block might be an acceptable optimisation (not really sure about it). – Gorpik Oct 25 '12 at 11:58
  • @Gorpik: the exception never escapes `A``s constructor, so I would say that it is a bug in the OP's example. – Yakov Galka Oct 25 '12 at 12:00
  • @Gorpik: Maybe. My compiler is VC :) Do you have an explanation why `catch(...)` in `main` was executed in OP's case? – Andriy Oct 25 '12 at 12:00
  • 2
    sorry about the miunderstanding, I didn't edit or approve any edit to my question, there was an change of code by the one that edited my code. – AlexDan Oct 25 '12 at 12:01
  • Yep, someone who doesn't know C++ edited the question. I rollbacked. – Yakov Galka Oct 25 '12 at 12:03
  • @ybungalobill: Sorry, we had a blackout. The explanation is a bit long, so I'll update my answer for that. – Gorpik Oct 25 '12 at 12:54