Reputation: 165
I read the book C++ How to Program 8th Edition by Paul Deitel. There is a statement at p.645:
When an exception is thrown from the constructor for an object that's created in a new expression, the dynamically allocated memory for that object is released.
To verify this statement, I wrote code as follows:
#include <iostream>
#include <exception>
#include <memory>
class A{
public:
A(){std::cout << "A is coming." << std::endl;}
~A(){std::cout << "A is leaving." << std::endl;}
};
class B
{
public:
B()
{
std::cout << "B is coming." << std::endl;
A b;
throw 3;
}
~B(){std::cout << "B is leaving." << std::endl;}
};
int main(void)
{
try
{
std::shared_ptr<B> pi(new B);
}
catch(...)
{
std::cout << "Exception handled!" << std::endl;
}
}
The output is:
B is coming.
A is coming.
A is leaving.
Exception handled!
This shows that B's destructor isn't invoked, which seems to conflict with the statement above.
Is my code correct to verify the statement? If not, how should I modify it? If yes, does it mean that the statement is wrong?
Upvotes: 3
Views: 2132
Reputation: 20823
It means that everything in the ctor of B
up to the point of the exception is destruction. An instance of B
itself was never constructed, therefore it must not be destructed. Note also that pi
was never constructed.
std::shared_ptr<B> pi(new B) - start with new B
new B - triggers the ctor of B
std::cout ... - the output
A b; - construct an A
throw 3; - calls ~A()
- rewind, new B is "aborted"
- std::shared_ptr<B> pi(new B) is "aborted"
You could modify your code to see, that the constructor of the std::shared_ptr
is never hit by replacing it with a new class of yours, taking a pointer:
struct T {
T(B*) { std::cout << "T::T()\n"; }
};
...
try
{
T pi(new B); // instead of std::shared_ptr<B> pi(new B);
}
...
The constructor of T
will not be hit (cf. "pi
was never constructed").
Now assume that the constructor of B
would allocate memory as in:
B()
{
A* a = new A(); // in contrast to A a;
throw 3;
}
were previously A::~A()
was called, that is a was deconstructed, we now have a pointer, and pointers don't need to be deconstructed. However the memory allocated and assigned to a is not deleted. (Had you used a smart pointer std::unique_ptr<A> a = std::make_unique<A>();
, the memory would have been released, because the destructor of std::unique_ptr<A>
is called and it will release the memory.)
Upvotes: 2
Reputation: 385088
You're confusing two things:
You've shown that the latter doesn't occur, which makes sense: how can you destroy something that wasn't properly constructed? Note that the member variables will have their destructors invoked, though, because by the time the constructor threw an exception all the member variables had been fully constructed.
But that has nothing to do with memory being released, which will assuredly occur.
[C++11: 15.2/2]:
An object of any storage duration whose initialization or destruction is terminated by an exception will have destructors executed for all of its fully constructed subobjects (excluding the variant members of a union-like class), that is, for subobjects for which the principal constructor (12.6.2) has completed execution and the destructor has not yet begun execution. Similarly, if the non-delegating constructor for an object has completed execution and a delegating constructor for that object exits with an exception, the object’s destructor will be invoked. If the object was allocated in a new-expression, the matching deallocation function (3.7.4.2, 5.3.4, 12.5), if any, is called to free the storage occupied by the object.
Upvotes: 5