Skip to main content
GameDev.net gamedev.net
🔒 Locked

C++ Exceptions

Started by nickwinters Jan 15, 2005 at 3:10 AM 13 replies 2.1k views
Original Post
nickwinters
nickwinters
I've been using exceptions in C# and Java, and have found them a really nice way to handle errors. I've done a google on C++ exceptions and found some tutorials. One thing I've noticed is that everyone creates their own exception class. Is there no exception class built into the language? In .NET, I've noticed that exceptions are very slow. How have people found their speed in C++? Thanks. -Nick
Dean Harding
Dean Harding
There are few standard exceptions in the std namespace (e.g. std::bad_alloc, etc) but it's not like C#/Java where there's a standard exception "base class". In C++, you can throw any type as an exception (for example, throw "an exception"; is perfectly legal).

Exceptions will always be fairly slow, since a lot of work needs to be done to unwind the stack (and ensure everything is properly destructed). However, the overhead is almost always when the exception is actually thrown - there shouldn't really be a noticeable hitwhen entering/leaving a try block. But if you keep exception for what they're meant for (exceptional circumstances, and not "normal" error conditions) then it won't make much difference.
risingdragon3
risingdragon3
In general, exceptions are implemented with a big ass table, so there is no overhead when using exceptions, at all, UNTIL an exception is actually thrown.

When the exception is thrown, the usual costs with unwinding the stack, yadedada, happen. But you already knew that.

Again, use exceptions only for exceptional stuff, and you should suffer no performance penalities.
blizzard999
blizzard999
Quote:
Original post by Anonymous Poster
It is no 'slower' than leaving any other scope..


I agree...and obviously an 'exception' should be an exception :)

You can also disable them in release mode so you can have 0% overhead (the real overhead is caused by user routine for trace the exception and by assert statements)(throwing an exception is nothing else than a "destroy the stack and goto line")

See also this
Sharlin
Sharlin
Actually, there is a standard exception base class: std::exception in . All standard library exceptions are derived from it, and although, as has been said, in C++ you can throw anything, even built-in types, it is good practice to derive your own exception classes from it.
BigBadBob
BigBadBob
it should also be noted that there is an additional cost other than stack unwinding...exceptions are thrown by value and there are actually two copies that happen. The object you throw is copied first like a return type and then copied again into the variable of a catch block.
Sharlin
Sharlin
Hmm... what if you catch by reference? Isn't that the recommended way anyway, because that way you get polymorphic behaviour? Consider:

struct base { virtual void foo() { cout << "base\n"; } };struct derived { virtual void foo() { cout << "derived\n"; } };...try {  throw derived();}catch(base &b){  b.foo();}// outputs "derived"
markr
markr
If your profiler tells you that you're spending a significant amount of time handling exceptions, you're throwing them too often.

For example, don't throw an exception when you try to move your character right and it hits a wall - that isn't an "exceptional enough" case.

Personally I only use them for bad things - mostly unrecoverable errors (like unable to load a level file, or if something which should never happen happens)

Because my exception handler displays a message then quits, my program is likely to throw a maximum of one exception during its lifetime (unless the exception handler throws another)

Mark
Dark Star
Dark Star
Yo Nickwinters,

I think the reason why exceptions are slow in .NET is because the JIT kicks in when it first handles it. After that exception is done, its faster afer.

Thats something that I read but don't quote me on it, I may be a littl wrong


DarkStar
UK
---------------------------------------------You Only Live Once - Don't be afriad to take chances.
BigBadBob
BigBadBob
Quote:
Original post by Sharlin
Hmm... what if you catch by reference? Isn't that the recommended way anyway, because that way you get polymorphic behaviour?
Yes you should always catch Exceptions by reference to facilitate use of an exception inheritance hierachy but also to decrease the number of object copies. As I said above, catch by value for an exception involves two object copies whereas catch by reference involves only one. The object that is thrown is ALWAYS COPIED ONCE regardless of whether the catch site uses a reference or not.
Enigma
Enigma
Quote:
Original post by BigBadBob
The object that is thrown is ALWAYS COPIED ONCE regardless of whether the catch site uses a reference or not.


Oh, really?
#include <iostream>class Exception{	public:		Exception(){++constructs;}		Exception(const Exception&){++copies;}		~Exception(){++destructs;}		Exception& operator=(const Exception&){++assigns;return *this;}		static int constructs;		static int copies;		static int destructs;		static int assigns;};int Exception::constructs = 0;int Exception::copies = 0;int Exception::destructs = 0;int Exception::assigns = 0;int exceptionThrowingFunc(int p){	if (p == 2)	{		throw Exception();	}	return p + 2;}int main(int argc, char**){	int r;	try	{		r = exceptionThrowingFunc(argc);	}	catch (Exception& e)	{		r = int(&e);	}	std::cout << "Constructs: " << Exception::constructs << "\nCopies: " << Exception::copies;	std::cout << "\nDestructs: " << Exception::destructs << "\nAssigns: " << Exception::assigns << '\n';	return r;}

Results:
BCC:
Constructs: 1
Copies: 1
Destructs: 2
Assigns: 0
GCC:
Constructs: 1
Copies: 0
Destructs: 1
Assigns: 0
VC++:
Constructs: 1
Copies: 0
Destructs: 1
Assigns: 0


Looks pretty implementation dependant to me.

Enigma
nickwinters
nickwinters
Thanks guys, you've been really helpful.
Enigma
Enigma
Just found the clause in the standard which allows elimination of the temporary copy (15.1.5, final draft):
Quote:
C++ Final Draft Standard:
If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (class.temporary), then the exception in the handler can be initialized directly with the argument of the throw expression. When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated). Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).


Enigma
BigBadBob
BigBadBob
Quote:
Original post by Enigma
Just found the clause in the standard which allows elimination of the temporary copy (15.1.5, final draft):
Quote:
C++ Final Draft Standard:
If the use of the temporary object can be eliminated without changing the meaning of the program except for the execution of constructors and destructors associated with the use of the temporary object (class.temporary), then the exception in the handler can be initialized directly with the argument of the throw expression. When the thrown object is a class object, and the copy constructor used to initialize the temporary copy is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated). Similarly, if the destructor for that object is not accessible, the program is ill-formed (even when the temporary object could otherwise be eliminated).


Enigma
yes thats a valid point that holds (if im right) only when you do throw ExceptionClass(). If in your try block you did the following:

try
{
ExceptionClass e;
if(some_condition)
throw e;
}
catch(ExceptionClass& e) {}

Then there will be a copy of the variable e because the compiler will construct the exception first. Then copy it into a different memory space so that it still exists after the scope is exited. You may do this to set certain values of the exception before the if test. If the object is created before the try block it is my understanding that it will still be copied anyway and that the compiler does not perform scope checking to see if there will be a danger of the object not existing in the catch clause.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.