473,796 Members | 2,652 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

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<<en dl; }
~L() { cout<<"~L() at "<<this<<en dl; }
};

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

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

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"som e 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****@i nformatik.hu-berlin.de \
\ http://www.informatik.hu-berlin.de/~ahrens \
\ _______________ _______________ ______________\
Nov 29 '05 #1
6 1403
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<<en dl; }
~L() { cout<<"~L() at "<<this<<en dl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<en dl;
}
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<<en dl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"som e 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<<en dl; }
~L() { cout<<"~L() at "<<this<<en dl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<en dl;
}
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<<en dl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"som e 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****@i nformatik.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<<en dl; }
~L() { cout<<"~L() at "<<this<<en dl; }
};

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

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

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"som e 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<<en dl; }
~L() { cout<<"~L() at "<<this<<en dl; }
};

class X {
public:
L* l;
X() try : l(new L) {
cout<<"X() at "<<this<<en dl;
}
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<<en dl;
delete l;
}
};

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"som e 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<<en dl; }
~L() { cout<<"~L() at "<<this<<en dl; }
};

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

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

int main(){
try {
X* p = new X;
delete p;
}
catch(...) { std::cout<<"som e 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
1303
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 static linkage but fubars entirely with dynamic linkage. I'm guessing its a memory space issue because tracing the address &foobar shows a different value when its
10
1372
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
1304
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
2137
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 defined = 2;
2
1725
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; m_result = os.str(); }
4
2012
by: ksukhonosenko | last post by:
This message was originally posted to comp.lang.c++.moderated ---------------------------------------------------------------------------------------------- Hi! I face a problem in my production code. I could deduce this problem to program shown below. It seems, that try-block in constructor doesnt work as it should (compared to case where no try block exists at all). I tested this small program on my MSVC .NET Pro 2003 (and separately...
26
2195
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 perfectly.)
8
2743
by: Grizlyk | last post by:
Good morning. Look here: http://groups.google.com/group/fido7.ru.cpp.chainik/browse_frm/thread/7341aba5238c0f79 and here: http://groups.google.com/group/fido7.ru.cpp.chainik/browse_frm/thread/cb014f4ba9df614a Can anybody answer? Who can't read in russian:
285
8973
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/ specs gcc version 2.95.3 20010315 (release)
15
1837
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
9673
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9524
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10449
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
10168
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
10003
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
9047
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7546
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 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 a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5568
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
3
2924
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

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.