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:
- Return a constructed object
- 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()
.