473,322 Members | 1,610 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,322 software developers and data experts.

undef. behaviour in ctor-exc. handlers

hi all,

acoording to the c++ standard

"15.3.
- -10- Referring to any non-static member or base class of an object in
the handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior."

it is not allowed to refer to l (delete l;) in the following code

#include <iostream>
#include <memory>

using namespace std;

struct L {
L() { cout<<"L() at "<<this<<endl; }
~L() { cout<<"~L() at "<<this<<endl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<endl;
}
catch (...) { delete l; throw; }

~X(){
cout<<"~X() at "<<this<<endl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"some exception occured"<<std::endl; }
}

i wonder why this has been declared such, because it leads directly
to leaking local ressources. i know herb sutters arguments from 'more
exc. c++' (item 18) where a local pointer could refer to a meanwhile
destoyed subobject (as i understand it). but this seems to be a very
rare situation.

moreover compiled with g++ (3.3.5) the output is

L() at 0x804a018
X() at 0x804a008
~X() at 0x804a008
~L() at 0x804a018

without the leaking L

any idea ?

--
mfg
k ahrens
_____________________________________________
\ phone +49 30 2093 3113 \ :-) \
\ fax +49 30 2093 3112 \__________________\
\ mailto:ah****@informatik.hu-berlin.de \
\ http://www.informatik.hu-berlin.de/~ahrens \
\ ____________________________________________\
Nov 29 '05 #1
6 1385
Klaus Ahrens wrote:
hi all,

acoording to the c++ standard

"15.3.
- -10- Referring to any non-static member or base class of an object in
the handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior."

it is not allowed to refer to l (delete l;) in the following code

#include <iostream>
#include <memory>

using namespace std;

struct L {
L() { cout<<"L() at "<<this<<endl; }
~L() { cout<<"~L() at "<<this<<endl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<endl;
}
catch (...) { delete l; throw; }
This is not legal C++. If your compiler allows such syntax (as you
imply below), it is non-conformant.

~X(){
cout<<"~X() at "<<this<<endl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"some exception occured"<<std::endl; }
}

i wonder why this has been declared such, because it leads directly
to leaking local ressources. i know herb sutters arguments from 'more
exc. c++' (item 18) where a local pointer could refer to a meanwhile
destoyed subobject (as i understand it). but this seems to be a very
rare situation.

moreover compiled with g++ (3.3.5) the output is

L() at 0x804a018
X() at 0x804a008
~X() at 0x804a008
~L() at 0x804a018

without the leaking L
But your code doesn't throw an exception. Try adding a second member to
X and throwing an exception in the second member's constructor:

class MyException {};
class M { M() { throw MyException(); } };
class L {};
class X
{
L* l_;
M* m_;
public:
X()
try
: l_( new L ),
m_( new M )
{}
catch( const MyException& e )
{
// Automatic rethrow of e here
}
};

Now l_ is leaked.

any idea ?


Perhaps this is the same material from Herb Sutter that you're
referring to, but he says (http://www.gotw.ca/gotw/066.htm):

"[O]nce you get into your constructor try-block's handler, any local
variables in the constructor body are also already out of scope, and
you are guaranteed that no base subobjects or member objects exist any
more, period. You can't even refer to their names. Either the parts of
your object were never constructed, or those that were constructed have
already been destroyed. So you can't be cleaning up anything that
relies on referring to a base or member of the class (and anyway,
that's what the base and member destructors are for, right?)."

In practice, that means you should use a smart pointer such as
std::auto_ptr instead of a raw pointer for all class members to prevent
leaking local resources.

The same link discusses the rationale for why C++ does things that way,
and I'll just commend it for your reading pleasure rather than recap it
here.

Cheers! --M

Nov 29 '05 #2
mlimber schrieb:
Klaus Ahrens wrote:
hi all,

acoording to the c++ standard

"15.3.
- -10- Referring to any non-static member or base class of an object in
the handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior."

it is not allowed to refer to l (delete l;) in the following code

#include <iostream>
#include <memory>

using namespace std;

struct L {
L() { cout<<"L() at "<<this<<endl; }
~L() { cout<<"~L() at "<<this<<endl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<endl;
}
catch (...) { delete l; throw; }

This is not legal C++. If your compiler allows such syntax (as you
imply below), it is non-conformant.

~X(){
cout<<"~X() at "<<this<<endl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"some exception occured"<<std::endl; }
}

i wonder why this has been declared such, because it leads directly
to leaking local ressources. i know herb sutters arguments from 'more
exc. c++' (item 18) where a local pointer could refer to a meanwhile
destoyed subobject (as i understand it). but this seems to be a very
rare situation.

moreover compiled with g++ (3.3.5) the output is

L() at 0x804a018
X() at 0x804a008
~X() at 0x804a008
~L() at 0x804a018

without the leaking L

But your code doesn't throw an exception. Try adding a second member to
X and throwing an exception in the second member's constructor:

class MyException {};
class M { M() { throw MyException(); } };
class L {};
class X
{
L* l_;
M* m_;
public:
X()
try
: l_( new L ),
m_( new M )
{}
catch( const MyException& e )
{
// Automatic rethrow of e here
}
};

Now l_ is leaked.

any idea ?

Perhaps this is the same material from Herb Sutter that you're
referring to, but he says (http://www.gotw.ca/gotw/066.htm):

"[O]nce you get into your constructor try-block's handler, any local
variables in the constructor body are also already out of scope, and
you are guaranteed that no base subobjects or member objects exist any
more, period. You can't even refer to their names. Either the parts of
your object were never constructed, or those that were constructed have
already been destroyed. So you can't be cleaning up anything that
relies on referring to a base or member of the class (and anyway,
that's what the base and member destructors are for, right?)."

In practice, that means you should use a smart pointer such as
std::auto_ptr instead of a raw pointer for all class members to prevent
leaking local resources.

The same link discusses the rationale for why C++ does things that way,
and I'll just commend it for your reading pleasure rather than recap it
here.

Cheers! --M

i agree with your arguments with multiple ressources, indeed i forgot to
throw, but even with an exception in my X-ctor g++ produces (obviously
wrong):

L() at 0x804b018
X() at 0x804b008
~L() at 0x804b018
some exception occured

in general c++ guarantees leak-free constructor failures by deleting
memory of half baked objects (arrays) implicitly. why couldn't this be
extended to single-ressource-owning classes at least (as in my class X)???
--
mfg
k ahrens
_____________________________________________
\ phone +49 30 2093 3113 \ :-) \
\ fax +49 30 2093 3112 \__________________\
\ mailto:ah****@informatik.hu-berlin.de \
\ http://www.informatik.hu-berlin.de/~ahrens \
\ ____________________________________________\
Nov 29 '05 #3
* Klaus Ahrens:
hi all,

acoording to the c++ standard

"15.3.
- -10- Referring to any non-static member or base class of an object in
the handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior."

it is not allowed to refer to l (delete l;) in the following code

#include <iostream>
#include <memory>

using namespace std;

struct L {
L() { cout<<"L() at "<<this<<endl; }
~L() { cout<<"~L() at "<<this<<endl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<endl;
}
catch (...) { delete l; throw; }

~X(){
cout<<"~X() at "<<this<<endl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"some exception occured"<<std::endl; }
}

i wonder why this has been declared such, because it leads directly
to leaking local ressources.


No, the C++ rules don't.

The above program is invalid.

If you'd care to present a valid program and explain _why_ you think it
would leak, we can set you right (or perhaps it would leak, it's not
difficult to arrange for a program to leak memory).
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Nov 29 '05 #4
Klaus Ahrens wrote:
mlimber schrieb:
Klaus Ahrens wrote:
hi all,

acoording to the c++ standard

"15.3.
- -10- Referring to any non-static member or base class of an object in
the handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior."

it is not allowed to refer to l (delete l;) in the following code

#include <iostream>
#include <memory>

using namespace std;

struct L {
L() { cout<<"L() at "<<this<<endl; }
~L() { cout<<"~L() at "<<this<<endl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<endl;
}
catch (...) { delete l; throw; }

This is not legal C++. If your compiler allows such syntax (as you
imply below), it is non-conformant.

~X(){
cout<<"~X() at "<<this<<endl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"some exception occured"<<std::endl; }
}

i wonder why this has been declared such, because it leads directly
to leaking local ressources. i know herb sutters arguments from 'more
exc. c++' (item 18) where a local pointer could refer to a meanwhile
destoyed subobject (as i understand it). but this seems to be a very
rare situation.

moreover compiled with g++ (3.3.5) the output is

L() at 0x804a018
X() at 0x804a008
~X() at 0x804a008
~L() at 0x804a018

without the leaking L

But your code doesn't throw an exception. Try adding a second member to
X and throwing an exception in the second member's constructor:

class MyException {};
class M { M() { throw MyException(); } };
class L {};
class X
{
L* l_;
M* m_;
public:
X()
try
: l_( new L ),
m_( new M )
{}
catch( const MyException& e )
{
// Automatic rethrow of e here
}
};

Now l_ is leaked.

any idea ?

Perhaps this is the same material from Herb Sutter that you're
referring to, but he says (http://www.gotw.ca/gotw/066.htm):

"[O]nce you get into your constructor try-block's handler, any local
variables in the constructor body are also already out of scope, and
you are guaranteed that no base subobjects or member objects exist any
more, period. You can't even refer to their names. Either the parts of
your object were never constructed, or those that were constructed have
already been destroyed. So you can't be cleaning up anything that
relies on referring to a base or member of the class (and anyway,
that's what the base and member destructors are for, right?)."

In practice, that means you should use a smart pointer such as
std::auto_ptr instead of a raw pointer for all class members to prevent
leaking local resources.

The same link discusses the rationale for why C++ does things that way,
and I'll just commend it for your reading pleasure rather than recap it
here.

Cheers! --M

i agree with your arguments with multiple ressources, indeed i forgot to
throw, but even with an exception in my X-ctor g++ produces (obviously
wrong):

L() at 0x804b018
X() at 0x804b008
~L() at 0x804b018
some exception occured

in general c++ guarantees leak-free constructor failures by deleting
memory of half baked objects (arrays) implicitly. why couldn't this be
extended to single-ressource-owning classes at least (as in my class X)???


All constructed members of a class *are* implicitly destroyed when the
constructor (or any function) throws an exception, and that inclues raw
pointers that happen to be members. However, the standard rules for
pointers is that if the pointer is non-null and it goes out of scope
without being deleted (or somehow passing ownership), the object it
pointed to is leaked. A simpler example:

void Foo()
{
L* rawPtr = new L;
std::auto_ptr<L> autoPtr( new L );
throw MyException();
}

In this code, rawPtr is leaked but autoPtr is not. The two key benefits
of smart pointers are that they free the programmer from having to
manually delete each new-ed object and that they provide exception
safety. Their use is not only good practice in the example with M and L
above; it is essential for a non-leaking program.

Cheers! --M

Nov 29 '05 #5
Alf P. Steinbach schrieb:
* Klaus Ahrens:
hi all,

acoording to the c++ standard

"15.3.
- -10- Referring to any non-static member or base class of an object in
the handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior."

it is not allowed to refer to l (delete l;) in the following code

#include <iostream>
#include <memory>

using namespace std;

struct L {
L() { cout<<"L() at "<<this<<endl; }
~L() { cout<<"~L() at "<<this<<endl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<endl;
}
catch (...) { delete l; throw; }

~X(){
cout<<"~X() at "<<this<<endl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"some exception occured"<<std::endl; }
}

i wonder why this has been declared such, because it leads directly
to leaking local ressources.

No, the C++ rules don't.

The above program is invalid.


i know this for sure, and i know that auto_ptrs are much better, but my
question is concerned with the rationale behind that rule !

If you'd care to present a valid program and explain _why_ you think it
would leak, we can set you right (or perhaps it would leak, it's not
difficult to arrange for a program to leak memory).

Nov 29 '05 #6
* Klaus Ahrens:

i know this for sure, and i know that auto_ptrs are much better, but my
question is concerned with the rationale behind that rule !


Presumably you mean the rule 15.3/10 that if you refer to a non-static
member variable in a constructor's function try-block catch clause,
you're invoking Undefined Behavior

The reason is very simple: when you enter the catch clause the member
has already been destroyed, it does not exist anymore, or alternatively
it wasn't fully constructed, and did not ever come into existence; see
15.3/11.

Essentially a constructor function try-block is only useful for
converting a non-standard exception originating in the constructor's
initialization list, to a standard exception. The purpose of C++
'catch' is _not_ to do cleanup. The purpose is to fulfill contracts,
such as a constract about only throwing standard exceptions.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Nov 29 '05 #7

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: vincent ngai | last post by:
Hi folks, I have the following scenario Lib X A lib with 1 class and 1 template class with a static var, the ctor of the class initialises the templated class's var. This works if Lib X has...
10
by: richardclay09 | last post by:
The output of this: #include <iostream> #include <vector> using namespace std; struct X { int i; X(const X& x) : i(x.i) { cout << "ctor copy: " << i << endl;
5
by: Levent | last post by:
Hi, When compiled with gcc 3.3.3 and lower on various systems (tried cygwin, linux, aix) the following code behaves strangely: #include <iostream> class Foo { public: typedef int subs;
13
by: Chris Croughton | last post by:
Is the following code standard-compliant, and if so what should it do? And where in the standard defines the behaviour? #include <stdio.h> #define DEF defined XXX int main(void) { int...
2
by: shaun roe | last post by:
Following up from my earlier post, where I pursued the line I outlined. Here is the MYFunc class implementation: MyFunc::MyFunc(int i):m_i(i){ ostringstream os; os<<"This number is "<<m_i;...
4
by: ksukhonosenko | last post by:
This message was originally posted to comp.lang.c++.moderated ---------------------------------------------------------------------------------------------- Hi! I face a problem in my...
26
by: Frederick Gotham | last post by:
I have a general idea of the different kinds of behaviour described by the C Standard, such as: (1) Well-defined behaviour: int a = 2, b = 3; int c = a + b; (Jist: The code will work...
8
by: Grizlyk | last post by:
Good morning. Look here: http://groups.google.com/group/fido7.ru.cpp.chainik/browse_frm/thread/7341aba5238c0f79 and here:...
285
by: Sheth Raxit | last post by:
Machine 1 : bash-3.00$ uname -a SunOS <hostname5.10 Generic_118822-30 sun4u sparc SUNW,Sun-Fire-280R bash-3.00$ gcc -v Reading specs from /usr/local/lib/gcc-lib/sparc-sun-solaris2.8/2.95.3/...
15
by: subramanian100in | last post by:
consider the following program: #include <iostream> using namespace std; class Test { public: Test(int xx) : x(xx) { cout << x << endl; }
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.