Posted: Sun Aug 07, 2005 1:34 am Post subject: Exception safety with object being initialized
Hello!
I haven't dealt much with the issue of exception safety yet, so I am quite a
newbie to this area. I do know what happens when a constructor throws an
exceptions, but I am not sure about initialization. Please consider the
following code:
#include <iostream>
#include <memory>
using std::cout;
using std::endl;
struct Object
{
Object( int i ) {}
};
int f() { throw 0; }
int main()
{
try
{
std::auto_ptr<Object> p( new Object( f() ) );
}
catch ( int i )
{
cout << i << endl;
}
return 0;
}
The initialization of 'Object' will cause an exception to be thrown. I would
expect my compiler to allocate memory for 'Object' after returning from
'f()', so the exception should be thrown before the memory is allocated.
However, I don't know if the compiler is allowed to allocate memory for
'Object' before entering 'f()' and throwing the exception, and in that case,
I wonder if that memory will be released again.
Can anyone please clarify this for me or point me to the relevant section of
the standard?
Posted: Sun Aug 07, 2005 10:45 am Post subject: Re: Exception safety with object being initialized
Matthias Hofmann wrote:
Quote:
Hello!
I haven't dealt much with the issue of exception safety yet, so I am quite a
newbie to this area. I do know what happens when a constructor throws an
exceptions, but I am not sure about initialization. Please consider the
following code:
#include <iostream
#include
using std::cout;
using std::endl;
struct Object
{
Object( int i ) {}
};
int f() { throw 0; }
int main()
{
try
{
std::auto_ptr
}
catch ( int i )
{
cout << i << endl;
}
return 0;
}
The initialization of 'Object' will cause an exception to be thrown. I would
expect my compiler to allocate memory for 'Object' after returning from
'f()', so the exception should be thrown before the memory is allocated.
However, I don't know if the compiler is allowed to allocate memory for
'Object' before entering 'f()' and throwing the exception, and in that case,
I wonder if that memory will be released again.
There are two steps involved with new statement. The first is
allocating memory using operator new (which is not the same thing as
new statement), the second is invoking a constructor. If the latter
fails by throwing an exception, operator delete will be called to free
the allocated memory.
Quote:
Can anyone please clarify this for me or point me to the relevant section of the standard?
Posted: Mon Aug 08, 2005 7:46 am Post subject: Re: Exception safety with object being initialized
"Maxim Yegorushkin" <maxim.yegorushkin (AT) gmail (DOT) com> schrieb im Newsbeitrag
news:1123405998.634781.166230 (AT) z14g2000cwz (DOT) googlegroups.com...
Quote:
There are two steps involved with new statement. The first is
allocating memory using operator new (which is not the same thing as
new statement), the second is invoking a constructor. If the latter
fails by throwing an exception, operator delete will be called to free
the allocated memory.
Yes, but what if the exception is not thrown by the constructor, but by a
function whose return value is used to initialize the constructor? Let's
take a look at the relevant line of my code:
std::auto_ptr<Object> p( new Object( f() ) );
Is the compiler allowed to allocate memory before f() is entered, and if so,
will the memory be freed if f() throws an exception?
Posted: Mon Aug 08, 2005 7:49 am Post subject: Re: Exception safety with object being initialized
Matthias Hofmann wrote:
Quote:
Hello!
I haven't dealt much with the issue of exception safety yet, so I am quite a
newbie to this area. I do know what happens when a constructor throws an
exceptions, but I am not sure about initialization. Please consider the
following code:
#include <iostream
#include
using std::cout;
using std::endl;
struct Object
{
Object( int i ) {}
};
int f() { throw 0; }
int main()
{
try
{
std::auto_ptr
}
catch ( int i )
{
cout << i << endl;
}
return 0;
}
The initialization of 'Object' will cause an exception to be thrown. I would
expect my compiler to allocate memory for 'Object' after returning from
'f()', so the exception should be thrown before the memory is allocated.
However, I don't know if the compiler is allowed to allocate memory for
'Object' before entering 'f()' and throwing the exception, and in that case,
I wonder if that memory will be released again.
Can anyone please clarify this for me or point me to the relevant section of
the standard?
The following example might clarify things for you. I have also added a
passage from the standard which applies here. As you can see, there is
nothing for you to worry about, but be mindful of other related
problems as demonstrated by the last test.
void test3()
{
raii_ptr<D> pb(new D(new B, new C));
}
int main()
{
TEST(test1);
TEST(test2);
TEST(test3);
return 0;
}
/*
output:
[test1]
B::new
B::delete
throw.cpp(116): test1 failed with an exception:
throw.cpp(1: f() failed
[test2]
B::new
B::delete
throw.cpp(117): test2 failed with an exception:
throw.cpp(1: f() failed
[test3]
D::new
C::new <== leakage
B::new
B::delete
D::delete
throw.cpp(118): test3 failed with an exception:
throw.cpp(1: f() failed
ISO/IEC 14882:2003(E)
5.3.4 New
page 82
If any part of the object initialization described above*71) terminates
by
throwing an exception and a suitable deallocation function can be
found, the
deallocation function is called to free the memory in which the object
was
being constructed, after which the exception continues to propagate in
the
context of the new-expression. If no unambiguous matching deallocation
function can be found, propagating the exception does not cause the
object's
memory to be freed. [Note: This is appropriate when the called
allocation
function does not allocate memory; otherwise, it is likely to result in
a
memory leak. ]
*71) This may include evaluating a new-initializer and/or calling a
constructor.
*/
Posted: Mon Aug 08, 2005 10:26 am Post subject: Re: Exception safety with object being initialized
Matthias Hofmann wrote:
Quote:
"Maxim Yegorushkin" <maxim.yegorushkin (AT) gmail (DOT) com> schrieb im Newsbeitrag
news:1123405998.634781.166230 (AT) z14g2000cwz (DOT) googlegroups.com...
There are two steps involved with new statement. The first is
allocating memory using operator new (which is not the same thing as
new statement), the second is invoking a constructor. If the latter
fails by throwing an exception, operator delete will be called to free
the allocated memory.
Yes, but what if the exception is not thrown by the constructor, but by a
function whose return value is used to initialize the constructor? Let's
take a look at the relevant line of my code:
std::auto_ptr<Object> p( new Object( f() ) );
Is the compiler allowed to allocate memory before f() is entered,
It's allowed.
Quote:
and if so, will the memory be freed if f() throws an exception?
f() is an initializer expression for Object. As Rutger van Beusekom
quoted:
If *any part of the object initialization* described above terminates
by
throwing an exception...
Posted: Mon Aug 08, 2005 3:12 pm Post subject: Re: Exception safety with object being initialized
----- Original Message -----
From: "Rutger van Beusekom" <Rutger.van.Beusekom (AT) gmail (DOT) com>
Newsgroups: comp.lang.c++.moderated
To: <Usenet>
Sent: Monday, August 08, 2005 9:49 AM
Subject: Re: Exception safety with object being initialized
Quote:
The following example might clarify things for you. I have also added a
passage from the standard which applies here. As you can see, there is
nothing for you to worry about, but be mindful of other related
problems as demonstrated by the last test.
[skipping example]
Thank you very much for your explanation, it helped me quite a lot. It also
helped me to brush up my knowledge about allocation and deallocation
functions, which is a very confusing matter, especially because the related
information is scattered all over the standard...
There is still one thing that puzzles me. Let's take a look at a class from
your code:
When deleting an object of type B, B::operator delete( void*, size_t ) will
be called. The standard guarantees that in this case, the second argument
will be the size of the block of memory to be deallocated. However, I found
no such guarantee for the case when B::operator delete( void*, size_t ) is
called by a new-expression - is that an oversight? What value will be passed
for the second argument of B::operator delete( void*, size_t ) if an
exception is thrown while creating a new B?
When deleting an object of type B, B::operator delete( void*, size_t ) will
be called. The standard guarantees that in this case, the second argument
will be the size of the block of memory to be deallocated. However, I found
no such guarantee for the case when B::operator delete( void*, size_t ) is
called by a new-expression - is that an oversight? What value will be passed
for the second argument of B::operator delete( void*, size_t ) if an
exception is thrown while creating a new B?
Hi Matthias
The previously quoted passage of the standard mentions:
.... and a suitable deallocation function can be found, the deallocation
function is called to free the memory in which the object was being
constructed ...
and at
12.5 Free Store
page 198
item 5
.... the size of the block as its second argument.*105)
*105) If the static type in the delete-expression is different from the
dynamic type and the destructor is not virtual the size might be
incorrect, but that case is already undefined; see 5.3.5
Therefore, when delete is called (either explicitly by hand or
implicitly by new when an exception is thrown) and an appropriate
deallocation function is found, the size of the static type or dynamic
type (in case of a virtual dtor) is passed as the second argument.
[ B* pc = new C; delete pc ]
B::operator new(12)
B::ctor
C::ctor
C::dtor <== missing when compiled with -DVIRTUAL=''
B::dtor
B::operator delete(12) <== when compiled with -DVIRTUAL=virtual
B::operator delete( <== when compiled with -DVIRTUAL=''
Posted: Tue Aug 09, 2005 1:53 pm Post subject: Re: Exception safety with object being initialized
"Rutger van Beusekom" <Rutger.van.Beusekom (AT) gmail (DOT) com> schrieb im Newsbeitrag
news:1123526187.213471.103860 (AT) o13g2000cwo (DOT) googlegroups.com...
Quote:
... the size of the block as its second argument.*105)
*105) If the static type in the delete-expression is different from the
dynamic type and the destructor is not virtual the size might be
incorrect, but that case is already undefined; see 5.3.5
You should have quoted paragraph 5 in it entirety:
"When a delete-expression is executed, the selected deallocation function
shall be called with the address of the block of storage to be reclaimed as
its first argument and (if the two-parameter style is used) the size of the
block as its second argument."
Note that it says: "When a delete-expression is executed...", it does not
say anything about the case when a new-expression calls a deallocation
function.
Quote:
Therefore, when delete is called (either explicitly by hand or
implicitly by new when an exception is thrown) and an appropriate
deallocation function is found, the size of the static type or dynamic
type (in case of a virtual dtor) is passed as the second argument.
You cannot call delete because it is an operator. There is a delete
expression which calls a deallocation function. A deallocation function is
also called when a new-expression throws an exception. However, the called
deallocation functions are not the necessariliy the same, and the guarantee
about passing the size of the block as its second argument is only given for
the case of the delete-expression.
Quote:
Quad erat demonstrandum:
You code only proves the behaviour of *your* compiler.
There are a couple of things about deallocation functions in the standard
that seem unclear to me. For example, 3.7.3.2 says in paragraph 2 that the
following two dealocation functions are non-placement forms when used as
class members:
You may notice that the latter deallocation function is used as a placement
deallocation function, although it is defined to be a non-placement
deallocation function in 3.7.3.2/2. Strange, isn't it?
Another thing I find rather curious is that the standard does not define
which deallocation function a delete-expression will call when a class has
several of them. 12.5/4 only says that the compiler will look for a function
called "operator delete" and that the program is ill-formed "if the result
of the lookup is ambigous or inaccessible, or if the lookup selects a
placement deallocation function". But what makes such a lookup ambigous and
when does it select a placement function?
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum