What to throw?

K

Ken

Hi. I know it is recommended that exceptions should be caught by
reference, but are there recommendations for how an exception should
be thrown? Or is it just a matter of throwing a type that is
compatiple with the catch. If that is the case, I suppose that if you
are catching a reference to exception e, the exception being thrown
better be an instance of e or a reference to an instance of e (ie., if
you threw a pointer to e and caught a reference to e, the compiler
would generate an error).

Thanks for any clarification and/or correction,

Ken
 
V

Victor Bazarov

Ken said:
Hi. I know it is recommended that exceptions should be caught by
reference, but are there recommendations for how an exception should
be thrown? Or is it just a matter of throwing a type that is
compatiple with the catch. If that is the case, I suppose that if you
are catching a reference to exception e, the exception being thrown
better be an instance of e or a reference to an instance of e (ie., if
you threw a pointer to e and caught a reference to e, the compiler
would generate an error).

You got the compatibility bit correctly. But just like in the rest
of the language, "compatible" (or, correctly, "convertible") is more
than just "the same". You can throw a object of a class derived
from what you're going to catch by reference, for example.

Also, the compiler shouldn't generate an error if you throw a pointer
when trying to catch a reference. Somebody else might be expecting
that pointer. You will likely get "uncaught exception" during run-
time.

------------------------------------ example
struct A {};
struct B : A {};

void foo() { throw B(); }
void bar() {}

int main() {
try {
foo();
}
catch (A&) {
bar(); // you should end up here during execution
}
return 0;
}

------------------------------------ another example:
struct A {};
struct B : A {};

void foo() { throw new B(); }
void bar() {}

int main() {
try {
foo();
}
catch (A&) {
bar(); // maybe somebody will throw a reference...
}
catch (A*) {
bar(); // you should end up here because it's really a B*
}
return 0;
}
 
K

Ken

Thanks so much for the examples!

Just one follow-up: In the example below, let's say I replaced foo
with:

void foo()
{
B *myB;
myB = new B();
throw *myB;
}

Would this get caught by the reference catch (as opposed to the
pointer)? If so, does this mean that the copy made for the throw
would be a copy of the B instance as opposed to a copy of the pointer?
If so, I'm wondering if this is what I really need to do to avoid a
memory leak:

void foo()
{
B *myB;
myB = new B();
throw *myB;
delete myB;
}

On the otherhand, I'm thinking that the best way to do this is:

void foo()
{
B myB;
throw myB;
}

My assumption is that this, too, would get caught by the reference
catch statement. Is this correct?

Also, are there any advantages to throwing a pointer instead? Looking
at all this, it seems to me like a general rule of thumb would be to
just throw by value and catch by reference. Does this seem right?

Thanks again for any input!

Ken
 
V

Victor Bazarov

Ken said:
Victor Bazarov <[email protected]> wrote in message
Thanks so much for the examples!

Just one follow-up: In the example below, let's say I replaced foo
with:

void foo()
{
B *myB;
myB = new B();
throw *myB;
}

Would this get caught by the reference catch (as opposed to the
pointer)?

Of course! The type of *myB is l-value of B, and as such can be bound
to B&.
If so, does this mean that the copy made for the throw
would be a copy of the B instance as opposed to a copy of the pointer?

There is no copy made for the throw. Why would there be?
If so, I'm wondering if this is what I really need to do to avoid a
memory leak:

void foo()
{
B *myB;
myB = new B();
throw *myB;
delete myB;

}

On the otherhand, I'm thinking that the best way to do this is:

void foo()
{
B myB;
throw myB;

Or, simply

throw B(); // no need to declare the local object
}

My assumption is that this, too, would get caught by the reference
catch statement. Is this correct?

I believe so.
Also, are there any advantages to throwing a pointer instead? Looking
at all this, it seems to me like a general rule of thumb would be to
just throw by value and catch by reference. Does this seem right?

Throwing a pointer does indeed seem unnecessary. However, if you
want to be simple you can always do

...
throw "such and such error";

...
catch (char const* errormsg) // catching a pointer
{
cerr << errormsg;
...
}

V
 
K

Ken

Victor Bazarov said:
There is no copy made for the throw. Why would there be?

I believe that C++ creates a temporary copy of the exception object
being thrown.

See http://www.devx.com/tips/Tip/12707. This says that:

"...when an exception is thrown, the mechanism first searches for an
appropriate handler in the current scope. If such handler does not
exist, all local objects are destroyed, the exception object itself is
copied on the stack of the function that is higher in the calling
chain, and only then is the current scope exited."

So behind the scenes, it is a copy of the exception object that goes
the next level up.

- Ken
 
D

Denis Remezov

Ken said:
I believe that C++ creates a temporary copy of the exception object
being thrown.

It may, but it doesn't have to. Temporaries may be optimised away by
the compiler, though you cannot make much use of that fact. In
particular, the object thrown must be still destructible and
copy-constructible; also, you cannot rely on the object that you catch
(even if by reference) being the same as the one thrown.

With every compiler I have, when catching by reference, no copies are
created. If I try catching by value rather than by reference, a
temporary for the catch statement is copy-constructed, but I believe
it too is allowed to be eliminated if it is const.

See http://www.devx.com/tips/Tip/12707. This says that:

"...when an exception is thrown, the mechanism first searches for an
appropriate handler in the current scope. If such handler does not
exist, all local objects are destroyed, the exception object itself is
copied on the stack of the function that is higher in the calling
chain, and only then is the current scope exited."

The stack is only guaranteed to be unwound if an appropriate handler
is found in some enclosing scope. If not, it may or may not be
unwound (and, accordingly, local objects may or may not be destructed)
before terminate() is called.


Denis
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top