Topic: Exceptions from Destructors
Author: David R Tribble <david.tribble@central.beasys.com>
Date: 1997/06/04 Raw View
kjhopps@mmm.com wrote:
> I have a question regarding exceptions from destructors. I cannot find
> a reference in the [proposed] standard that addresses this question.
>
> Consider this:
> Thing* array = new Thing[3];
> delete [] array;
> What happens if the destructor for array[1] throws?
I asked the same question a couple of months ago. The best response was
from Andrew Koenig, which I reproduce below, stating that this is best
considered as 'undefined behavior':
---
>Subject: Re: Can delete throw an exception?
>Date: 10 Feb 1997 10:56:59 PST
>From: ark@research.att.com (Andrew Koenig)
>Newsgroups: comp.std.c++
>References: <2.2.32.19970206200249.002b04d0@central.beasys.com>
In article <2.2.32.19970206200249.002b04d0@central.beasys.com>
David R Tribble <david.tribble@central.beasys.com> writes:
> 1) Can operator 'delete' throw an exception if it detects a corrupt free
> store?
Yes, by slightly convoluted reasoning:
1. There is no officially sanctioned way of corrupting free store.
2. Therefore, in order to corrupt free store, a program must have
done something that evoked ``undefined behavior.''
3. Once that has happened, all bets are off -- any operation can
potentially throw an exception, regardless of what the standard
might say about what it is supposed to do.
---
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1997/06/04 Raw View
On 04 Jun 1997 10:49:10 PDT, David R Tribble
<david.tribble@central.beasys.com> wrote:
: kjhopps@mmm.com wrote:
: > I have a question regarding exceptions from destructors. I cannot find
: > a reference in the [proposed] standard that addresses this question.
: >
: > Consider this:
: > Thing* array = new Thing[3];
: > delete [] array;
: > What happens if the destructor for array[1] throws?
: I asked the same question a couple of months ago. The best response was
: from Andrew Koenig, which I reproduce below, stating that this is best
: considered as 'undefined behavior':
I remember the question and response well. It had nothing to do with
Kevin's question. You asked if operator delete could throw. Kevin is
asking if a dtor can throw and have it propagate through a delete
expresion and if all of the destructors get called and if the operator
delete[] happens.
I added a new[], delete[] to the simple program posted earlier and got
the same kind of results. One implementation destructed only till the
throw while the other destructed all of the array elements.
Whether either implementation called operator delete[] is left as an
excerise for the reader. Hint: the implementation which did not
destruct all items is very popular and available on almost all
platforms. The other is any of many platform specific implementations
(more than one tried).
The real question is still, "What does the CD say about it?"
John
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: "Paul D. DeRocco" <pderocco@ix.netcom.crud.com>
Date: 1997/06/05 Raw View
David R Tribble wrote:
>
> In article <2.2.32.19970206200249.002b04d0@central.beasys.com>
> David R Tribble <david.tribble@central.beasys.com> writes:
>
> > 1) Can operator 'delete' throw an exception if it detects a corrupt free
> > store?
If you agree that destructors shouldn't throw exceptions, then allowing
operator delete to throw exceptions would require that
auto_ptr::~auto_ptr contain a try block, which would make it too
cumbersome to use. I'd therefore extend the rule to operator delete as
well.
--
Ciao,
Paul
(Please remove the extra "crud" from the return address,
which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/06/09 Raw View
jpotter@falcon.lhup.edu (John Potter) writes:
|> On 04 Jun 1997 10:49:10 PDT, David R Tribble
|> <david.tribble@central.beasys.com> wrote:
|>
|> : kjhopps@mmm.com wrote:
|> : > I have a question regarding exceptions from destructors. I cannot find
|> : > a reference in the [proposed] standard that addresses this question.
|> : >
|> : > Consider this:
|> : > Thing* array = new Thing[3];
|> : > delete [] array;
|> : > What happens if the destructor for array[1] throws?
|>
|> : I asked the same question a couple of months ago. The best response was
|> : from Andrew Koenig, which I reproduce below, stating that this is best
|> : considered as 'undefined behavior':
|>
|> I remember the question and response well. It had nothing to do with
|> Kevin's question. You asked if operator delete could throw. Kevin is
|> asking if a dtor can throw and have it propagate through a delete
|> expresion and if all of the destructors get called and if the operator
|> delete[] happens.
|>
|> I added a new[], delete[] to the simple program posted earlier and got
|> the same kind of results. One implementation destructed only till the
|> throw while the other destructed all of the array elements.
|>
|> Whether either implementation called operator delete[] is left as an
|> excerise for the reader. Hint: the implementation which did not
|> destruct all items is very popular and available on almost all
|> platforms. The other is any of many platform specific implementations
|> (more than one tried).
Well, I don't think that you can consider either "wrong".
|> The real question is still, "What does the CD say about it?"
To be decided?
--
James Kanze home: kanze@gabi-soft.fr +33 (0)1 39 55 85 62
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/06/09 Raw View
|> From: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
|> James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
|>
|> > struct A { A() ; ~A() ; } ;
|> > struct B : A { B() ; ~B() ; } ;
|> >
|> > struct C : B { C() ; ~C() ; } ;
|> ...
|> > {
|> > C* p = new C ;
|> > // ...
|> > delete p ;
|> > }
|> ...
|> >With regards to the case where B::~B() throws in the delete, it is
|> >interesting to look at the exact wording in the standard:
|>
|> The wording in the current draft just doesn't cover this case.
|> You can try to interpret the current wording as saying something
|> sensible about this case, but the extent to which it does or
|> does not is purely accidental.
Much hinges on the interpretation of what is meant by "partially
constructed object" in 15.2/2. This needs to be clarified.
|> Note that 5.3.5[expr.delete]/7 says "To free the storage pointed to, the
|> delete-expression will call a deallocation function." No ifs or buts,
|> and no talk about exceptions there, so one reasonable interpretation is
|> that it must always call a deallocation function, whether or not the
|> destructor throws an exception. But another reasonable interpretation
|> is that the standard does not say what happens if an exception is
|> thrown, therefore if an exception is thrown, the behaviour is
|> undefined.
This seems like grabbing at straws to me. I suspect that there are
lot's of similar cases in the draft: are there any ifs and buts in the
description of the order of initialization? But we know that it won't
be respected if one of the base classes throws.
|> The current draft is definitely inadequate when it comes to specifying
|> what happens when destructors throw exceptions. It ought to be fixed.
|>
|> >2. The implementation is tricky, to say the least, since not only can
|> >the new and the delete be in different modules, different control flows
|> >may cause the same delete to be executed for different new expressions.
|> >Basically, the implementation must somehow "memorize" the delete that is
|> >to be called.
|>
|> That is definitely not the intent of the committee.
|> If the destructor throws, delete should call the same deallocation
|> function that it would have if the destructor had returned.
Agreed. Regretfully, this is not what the current draft says.
--
James Kanze home: kanze@gabi-soft.fr +33 (0)1 39 55 85 62
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: kjhopps@mmm.com
Date: 1997/06/02 Raw View
I have a question regarding exceptions from destructors. I cannot find
a reference in the [proposed] standard that addresses this question.
Consider this:
Thing* array = new Thing[3];
delete [] array;
What happens if the destructor for array[1] throws?
Is this the same question as what happens when destroying an object if
one of its sub-objects' destructors throws?
--
Kevin J. Hopps, Imation kjhopps@imation.com
My opinions are my own. I speak neither for Imation nor 3M.
Support the anti-spam amendment. See http://www.cauce.org
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: Alexander Krotoff <krotoff@such.srcc.msu.su>
Date: 1997/06/03 Raw View
To: std-c++@ncar.ucar.edu
Subject: Re: Exceptions from Destructors
Newsgroups: comp.std.c++
In-Reply-To: <338EE82D.5B8F@mmm.com>
Organization: Research Computer Center, Moscow State University
X-Newsreader: TIN [UNIX 1.3 unoff BETA 970424; i386 FreeBSD 2.2.2-RELEASE]
kmc> I have a question regarding exceptions from destructors. I cannot find
kmc> a reference in the [proposed] standard that addresses this question.
kmc> Consider this:
kmc> Thing* array = new Thing[3];
kmc> delete [] array;
kmc> What happens if the destructor for array[1] throws?
kmc> Is this the same question as what happens when destroying an object if
kmc> one of its sub-objects' destructors throws?
This question was discussed several weeks ago:
-----------------------------------------------------------------------------
From: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Newsgroups: comp.std.c++
Subject: Re: Exceptions and destructors
Date: 15 Apr 97 14:52:32 GMT
Organization: Comp Sci, University of Melbourne
Lines: 53
Approved: Fergus Henderson <fjh@cs.mu.oz.au>
Message-ID: <5j03fa$3qp@mulga.cs.mu.OZ.AU>
References: <rf5bu7gacq3.fsf@vx.cit.alcatel.fr>
NNTP-Posting-Host: murlibobo.cs.mu.oz.au
X-Original-Date: 15 Apr 1997 14:31:06 GMT
X-Auth: PGPMoose V1.1 PGP comp.std.c++
iQBFAgUBM1OWNuEDnX0m9pzZAQFiAAF+IoOoT6hR2cWt8mPKsdVE+RbKwnb501+n
EJYqRHJ7g077RIKGLTVpMtx+CGiLohKD
=N9zw
Originator: fjh@murlibobo.cs.mu.OZ.AU
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
> struct A { A() ; ~A() ; } ;
> struct B : A { B() ; ~B() ; } ;
>
> struct C : B { C() ; ~C() ; } ;
...
> {
> C* p = new C ;
> // ...
> delete p ;
> }
...
>With regards to the case where B::~B() throws in the delete, it is
>interesting to look at the exact wording in the standard:
The wording in the current draft just doesn't cover this case.
You can try to interpret the current wording as saying something
sensible about this case, but the extent to which it does or
does not is purely accidental.
Note that 5.3.5[expr.delete]/7 says "To free the storage pointed to, the
delete-expression will call a deallocation function." No ifs or buts,
and no talk about exceptions there, so one reasonable interpretation is
that it must always call a deallocation function, whether or not the
destructor throws an exception. But another reasonable interpretation
is that the standard does not say what happens if an exception is
thrown, therefore if an exception is thrown, the behaviour is
undefined.
The current draft is definitely inadequate when it comes to specifying
what happens when destructors throw exceptions. It ought to be fixed.
>2. The implementation is tricky, to say the least, since not only can
>the new and the delete be in different modules, different control flows
>may cause the same delete to be executed for different new expressions.
>Basically, the implementation must somehow "memorize" the delete that is
>to be called.
That is definitely not the intent of the committee.
If the destructor throws, delete should call the same deallocation
function that it would have if the destructor had returned.
--
Fergus Henderson <fjh@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3 | -- the last words of T. S. Garp.
-----------------------------------------------------------------------------
From: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Newsgroups: comp.std.c++
Subject: Exceptions and destructors
Date: 15 Apr 97 11:51:22 GMT
Organization: -
Lines: 140
Approved: Fergus Henderson <fjh@cs.mu.oz.au>
Message-ID: <rf5bu7gacq3.fsf@vx.cit.alcatel.fr>
NNTP-Posting-Host: mundook.cs.mu.oz.au
X-Original-Date: 15 Apr 1997 10:53:40 +0200
X-Mailer: Gnus v5.3/Emacs 19.34
X-Auth: PGPMoose V1.1 PGP comp.std.c++
iQBFAgUBM1NrvuEDnX0m9pzZAQEsHQF/WQjyS28tVTVdAYlv00yAUDx8goMlExrt
vO/KywwZgiMM/zvBTaR2R7gBhjsA5XB5
=p/oY
Originator: fjh@mundook.cs.mu.OZ.AU
In comp.lang.c++.moderated recently, there has been a discussion of
exception safety, and in particular, of what state an object is left in
when the destructor of a sub-object throws an exception. I'd like to
present my interpretation of what I think the draft standard says here,
and ask that any members of the committee who were involved with the
specification of exceptions criticize it. (This is a tentative
interpretation, and I'm not at all sure of it. Also, it's a bit long
because I am feeling my way around.)
(In the following, I will speak of the "standard". Whenever I use the
word standard, it is in fact the current committee draft that I am
referring to.)
First, I'm basing my interpretation mainly on paragraph 2 of section
15.2, which starts out: "An object that is partially constructed will
have destructors executed only for its fully constructed sub-objects."
The first point I see as important is that this concerns all composite
objects (all object which have sub-objects). Thus, given the following:
struct A { A() ; ~A() ; } ;
struct B : A { B() ; ~B() ; } ;
struct C : B { C() ; ~C() ; } ;
struct D { A a1 ; A a2 ; A a3 ; } ;
typedef A E[ 3 ] ;
The cases of C, D and E are exactly the same, and an exception thrown in
the constructor/destructor of the B sub-object of C, D::a2 or E[1] will
have exactly the same effect.
The second point is that as far as I can tell, the standard never
actually defines "partially constructed", so we have to base our
interpretation on standard English usage. I see in fact two possible
interpretations: 1) an object is "partially constructed" between the
moment the first constructor of a sub-object starts execution, and the
moment that the constructor of the complete object finishes, or 2) in
addition to 1, an object is also "partially constructed" between the
moment the destructor of the comlete object is entered and the moment
the last destructor of a sub-object finishes. Although a superficial
reading of the expression might suggest 1, I think that given the
overall implications of the standard (e.g.: a destructor "deconstructs"
the object), only 2 really fits in. In all other contexts (e.g.: RTTI,
virtual function call resolution), the destructors mirror exactly the
constructors. Following this line of reasoning, I would also suppose
that the sub-object ceases to be "fully constructed" as soon as its
destructor is entered.
Up until this point, I don't really think that there is much room for
discussion. Although the standard could be clearer, particularly with
regards to "partially constructed" and destructors, I think it is
adequate. Thus, for example, given the above declarations, and the
following:
{
C c ;
// ...
}
If the constructor B:B() of object c exits via an exception, the
destructor A::~A(), and no other destructors, will be called. This is
explicit in the draft. Similarly, if the destructor B::~B() of the
object c exits via an exception (when the object goes out of scope),
then A::~A() (and no others) will be called as part of the process of
stack unwinding. This is from the definition 2 of "partially
constructed", above. (Note that if A::~A() exits via an exception in
either case above, terminate will be called.)
Now consider a slight variant of the above:
{
C* p = new C ;
// ...
delete p ;
}
Here, we are concerned not only with the destructors which might be
called, but with what happens to the allocated memory. Again, the case
if the exception is thrown in the new is perfectly clear, as it is
treated explicitly in the standard.
With regards to the case where B::~B() throws in the delete, it is
interesting to look at the exact wording in the standard: "If the object
or array was allocated in a new-expression and the new-expression does
not contain a new-placement, the deallocation function is called to free
the storage occupied by the object; ..." Note that it does not say "If
the exception occurs in a new expression.." or anything similar. It
says "if the object or array was allocated in a new-expression...".
Now, the object being destructed in the expression "delete p", above,
was most definitly allocated in a new expression, so I would argue that
the deallocation function should be called.
This would seem clear enough, except for two things:
1. The only thing preventing it from applying to all exceptions which
occur within an object is the fact that it occurs in a paragraph that is
talking about "partially constructed objects." So we must come back to
the definition of "partially constructed", above.
2. The implementation is tricky, to say the least, since not only can
the new and the delete be in different modules, different control flows
may cause the same delete to be executed for different new expressions.
Basically, the implementation must somehow "memorize" the delete that is
to be called. This is fundamentally more difficult than memorizing the
number of destructors to call in delete[], in that the implementation
must not only memorize which operator delete to call, but must also
memorize any additional arguments which might be significant. It also
means that at compile time, the compiler doesn't know how many arguments
it will actually have to pass to delete. These problems are solvable,
but at significant cost to users who don't use placement new.
Thus, I'm not at all sure that this is the solution that the committee
actually intended. On the other hand, however, if it isn't, it is very
difficult to see how the user can handle the problem if the memory is
not automatically deleted. The user cannot call delete on the pointer
value again, since part of the object has already been destructed. (It
may be possible if the object has a virtual destructor, but this only
covers the case of inhertance, not of membership or array elements.) On
the other hand, the user cannot call operator delete directly to free
the memory, since 1) that would leave some sub-objects undestructed, and
2) in the case of arrays, he doesn't even know the address returned by
operator new.
(The more I think about it, the more I'm convinced that the easiest
solution for the committee, and a perfectly acceptable one for the user,
is to declare that all destructors have an implicit "throw ()" exception
specification. See Scott Meyers for the reasons why the user doesn't
want to exit a destructor via an exception.)
--
James Kanze home: kanze@gabi-soft.fr +33 (0)1 39 55 85 62
office: kanze@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
-----------------------------------------------------------------------------
Regards,
Alexander.
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1997/06/04 Raw View
On 02 Jun 1997 10:30:42 PDT, kjhopps@mmm.com wrote:
: I have a question regarding exceptions from destructors. I cannot find
: a reference in the [proposed] standard that addresses this question.
: Consider this:
: Thing* array = new Thing[3];
: delete [] array;
: What happens if the destructor for array[1] throws?
: Is this the same question as what happens when destroying an object if
: one of its sub-objects' destructors throws?
Here is a little test program which demonstrates Kevin's question. I
get different results on two implementations each of which supports
the views of one side in the discussion. What does the draft say?
John
#include <iostream.h>
struct S {
~S () { cout << " ~S\n"; }
};
struct T {
~T () { cout << " ~T\n"; throw 1; }
};
struct B {
~B () { cout << " ~B\n"; }
};
struct D : B {
~D () { cout << " ~D\n"; }
S s;
T t;
};
struct N {
N () : id( ++ count) { }
~N () { cout << id << "~N\n"; if (id == 3) throw 2; }
static int count;
int id;
};
int N::count = 0;
int main () {
try {
D d;
}
catch (int i) {
cout << "caught " << i << '\n';
}
try {
N n[5];
}
catch (int i) {
cout << "caught " << i << '\n';
}
return 0;
}
The destruction of the object continues view.
~D
~T
~S
~B
caught 1
5~N
4~N
3~N
2~N
1~N
caught 2
The destruction of the object aborts view.
~D
~T
caught 1
5~N
4~N
3~N
caught 2
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]