By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
444,246 Members | 1,515 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 444,246 IT Pros & Developers. It's quick & easy.

Exceptions in destructors

P: n/a
I know that if an exception is thrown from a destructor while unwinding
the stack because of another exception, terminate() is called (FAQ
17.3). How exactly does this rule work? Is it acceptable to both throw
/and/ catch an exception inside a destructor, as in the following code?

struct Foo
{
void finalize()
{
throw 1.0;
}
~Foo()
{
try
{
finalize();
}
catch (double d)
{}
}
};

void bar()
{
Foo f;
throw 1;
}

int main()
{
try
{
bar();
}
catch (int i)
{}
return 0;
}

Rennie deGraaf
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQFGNVl6IvU5mZP08HERAnkaAJ9rODkY55YL/ON+PXYS0ImI2K783QCgzAwr
6zQzt0Hkj6h8KYRFfI/xYw8=
=H9qr
-----END PGP SIGNATURE-----

Apr 30 '07 #1
Share this Question
Share on Google+
5 Replies


P: n/a
Rennie deGraaf wrote:
I know that if an exception is thrown from a destructor while unwinding
the stack because of another exception, terminate() is called (FAQ
17.3). How exactly does this rule work? Is it acceptable to both throw
/and/ catch an exception inside a destructor, as in the following code?

[code redacted]
Yes, the restriction is that the exception may not escape the destructor.

Apr 30 '07 #2

P: n/a

On 4/29/07 7:50 PM, in article f1**********@ensb.cpsc.ucalgary.ca, "Rennie
deGraaf" <de*****@cpsc.no-processed-pork.ucalgary.cawrote:
I know that if an exception is thrown from a destructor while unwinding
the stack because of another exception, terminate() is called (FAQ
17.3). How exactly does this rule work? Is it acceptable to both throw
/and/ catch an exception inside a destructor, as in the following code?

struct Foo
{
void finalize()
{
throw 1.0;
}
~Foo()
{
try
{
finalize();
}
catch (double d)
{}
}
};
The runtime calls terminate() if - during exception processing - another
thrown exception "escapes" from the destructor in which it was thrown.

So as long as the thrown exception in the code above is caught before Foo's
destructor exits, then terminate() will not be called. Therefore the above
code is technically safe - but it is also fragile. Specifically, Foo's
destructor assumes that finalize() will only ever throw a double - but
through there is nothing in finalize()'s implementation that enforces such a
restriction - nor is there anything in Foo's destructor to check whether its
assumption about the type of finalize()'s thrown exceptions - remains valid
since the last time it checked.

Greg

Apr 30 '07 #3

P: n/a
Greg Herlihy wrote:


On 4/29/07 7:50 PM, in article f1**********@ensb.cpsc.ucalgary.ca, "Rennie
deGraaf" <de*****@cpsc.no-processed-pork.ucalgary.cawrote:
>I know that if an exception is thrown from a destructor while unwinding
the stack because of another exception, terminate() is called (FAQ
17.3). How exactly does this rule work? Is it acceptable to both throw
/and/ catch an exception inside a destructor, as in the following code?

struct Foo
{
void finalize()
{
throw 1.0;
}
~Foo()
{
try
{
finalize();
}
catch (double d)
{}
}
};
The runtime calls terminate() if - during exception processing - another
thrown exception "escapes" from the destructor in which it was thrown.

So as long as the thrown exception in the code above is caught before Foo's
destructor exits, then terminate() will not be called. Therefore the above
code is technically safe - but it is also fragile. Specifically, Foo's
destructor assumes that finalize() will only ever throw a double - but
through there is nothing in finalize()'s implementation that enforces such a
restriction - nor is there anything in Foo's destructor to check whether its
assumption about the type of finalize()'s thrown exceptions - remains valid
since the last time it checked.

Greg
In other words, I really should add throw specifiers, like this:

struct Foo
{
void finalize() throw(double)
{
throw 1.0;
}
~Foo() throw()
{
try
{
finalize();
}
catch (double d)
{}
}
};

I suppose that it would generally be a good idea to /always/ tag my
destructors with throw() unless I have a good reason to do otherwise?
For that matter, are there any circumstances where it would be safe to
throw an exception from a destructor?

Rennie deGraaf
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)

iD8DBQFGNX7tIvU5mZP08HERAu/9AJ0R26LkBLs6hVMjMAVsH181cMjTtgCfa7p2
jCaJy2cRDZA0JQgMQbdslt0=
=u7Mx
-----END PGP SIGNATURE-----

Apr 30 '07 #4

P: n/a
On Apr 29, 10:30 pm, Rennie deGraaf <degr...@cpsc.no-processed-
pork.ucalgary.cawrote:
Greg Herlihy wrote:
On 4/29/07 7:50 PM, in article f13lhq$f3...@ensb.cpsc.ucalgary.ca, "Rennie
deGraaf" <degr...@cpsc.no-processed-pork.ucalgary.cawrote:
I know that if an exception is thrown from a destructor while unwinding
the stack because of another exception, terminate() is called (FAQ
17.3). How exactly does this rule work? Is it acceptable to both throw
/and/ catch an exception inside a destructor, as in the following code?
So as long as the thrown exception in the code above is caught before Foo's
destructor exits, then terminate() will not be called. Therefore the above
code is technically safe - but it is also fragile. Specifically, Foo's
destructor assumes that finalize() will only ever throw a double - but
through there is nothing in finalize()'s implementation that enforces such a
restriction - nor is there anything in Foo's destructor to check whether its
assumption about the type of finalize()'s thrown exceptions - remains valid
since the last time it checked.

In other words, I really should add throw specifiers, like this:

struct Foo
{
void finalize() throw(double)
{
throw 1.0;
}
~Foo() throw()
{
try
{
finalize();
}
catch (double d)
{}
}

};

I suppose that it would generally be a good idea to /always/ tag my
destructors with throw() unless I have a good reason to do otherwise?
Personally, I would not bother adding throw() to destructor
declarations. The time and effort for making such a change would
probably be better spent making sure that no destructor in the program
propagtes exceptions beyond its scope.

On the other hand, the exception specification for finalize() I would
keep. Even though C++ exception specifications are checked at runtime
and not compile-time - at least the double exception specification
serves to document a dependency on finalize()'s thrown exception type
- that previously was not documented anywhere.

For that matter, are there any circumstances where it would be safe to
throw an exception from a destructor?
Sure - in fact I would imagine that under most circumstances throwing
an exception from a destructor would be safe. The problem is that
there are always circumstances when throwing an exception from a
destructor is not safe - so what should the program do instead?

In other words, it does not make a lot of sense for a program to throw
an exception - unless it is safe to throw that exception every time
the program wants to throw it. Otherwise, the code paths of the
program need to split into two different error-handling paths - one
when the exception is thrown and another when it is not. So throwing
an exception from a destructor does not end up simplifying or
consolidating the program's error handling - instead the two execution
paths just makes the program more complicated for no reason.

Greg

Apr 30 '07 #5

P: n/a
On Apr 30, 7:30 am, Rennie deGraaf <degr...@cpsc.no-processed-
pork.ucalgary.cawrote:

[...]
In other words, I really should add throw specifiers, like this:
struct Foo
{
void finalize() throw(double)
{
throw 1.0;
}
~Foo() throw()
{
try
{
finalize();
}
catch (double d)
{}
}
};
I suppose that it would generally be a good idea to /always/ tag my
destructors with throw() unless I have a good reason to do otherwise?
For that matter, are there any circumstances where it would be safe to
throw an exception from a destructor?
Opinions with regards to the utility of exception specifiers
vary:-). In general, there's pretty much a consensus that
specifiers which list possible exceptions are useless; the only
possible effect they can have is to result in slower code.
There's much less consensus regarding empty specifiers, e.g.
throw(); knowing that a function cannot throw is often important
information, both to the user and to the optimizer.

In practice, with regards to the reader, I would generally
expect a destructor not to throw, and any other function to
throw std::exception, or anything derived from it, unless
otherwise documented. Throwing from a destructor should be so
exceptional that not just the possibility itself, but the
justification and reasons behind it should be documented; an
exception specifier is not sufficient for this. And because
non-throwing destructors are so common, you can certainly not
count on the absense of a specifier on the destructor to signal
that it might throw. So from the human reader's point of view,
an exception specifier on a destructor doesn't really add any
useful information. Just the reverse is true for other
functions, however; in the absense of other documentation, you
should assume that the function can throw. And a simple
"throw()" is excellent documentation that it cannot. Unlike the
case for destructors, I don't think you normally have to justify
why the function doesn't throw; it's sufficient to document
clearly that it doesn't.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Apr 30 '07 #6

This discussion thread is closed

Replies have been disabled for this discussion.