Klaus Ahrens wrote:[color=blue]
> mlimber schrieb:[color=green]
> > Klaus Ahrens wrote:
> >[color=darkred]
> >>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; }[/color]
> >
> >
> > This is not legal C++. If your compiler allows such syntax (as you
> > imply below), it is non-conformant.
> >
> >[color=darkred]
> >> ~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[/color]
> >
> >
> > 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.
> >
> >[color=darkred]
> >>any idea ?[/color]
> >
> >
> > 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
> >[/color]
> 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)???[/color]
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