TITLE: exceptions in a stack design (Newsgroups: comp.lang.c++.moderated, 6 Oct 97) SHEPPARD: Sheppard (xie@netcom.com) : I have a templated Stack class with ( among others ) these two : functions: : : template : int /* bool */ Stack::isEmpty() const { return numObjects == 0 }; : : template : T Stack::pop() : { : if ( isEmpty() ) throw stackEmpty; : : // otherwise return the next T object : // and remove it from the stack : } : : the stack also contains the following public enumerated type: : : enum StackErrors{ stackEmpty }; KUEHL: kuehl@horn.informatik.uni-konstanz.de (Dietmar Kuehl) Before discussing your actual question, here are two general comments: - The normal stack operation "pop" should be split up into two operations like eg. 'pop()' and 'top()', where the former removes the top element while 'top()' provides access to the current top element (if present) using a reference. Of course, 'pop()' does NOT return [a copy] of the remove element. Apart from better [expected; I have to admit, I haven't measured it] performance (there is no need to construct an object for the returned object), this also increases exception safety. Even if the ctor or the dtor of 'T' might throw an exception, 'pop()' and 'top()' can be implemented such that they would not corrupt the stack (for more discussion on this topic see eg. "Ruminations on C++", A.Koenig & B.Moo, Addison-Wesley; at least I think this issue is addressed there but I'm not sure...). - Exception Classes should probably be derived at least from 'std::exception' or, even better, from a matching class in the hierachy of exceptions defined in the standard C++ library. This makes it possible to catch exceptions and extract at least minimal information. SHEPPARD: : My question to you is, why is it ``better'' to test the status of the : queue explicitly rather than make use of the fact that pop() tests it : anyway. That is to say, why use this piece of code: : : while( !myStack.isEmpty() ) : { : someObj = myStack.pop(); : // then do something to the returned object : } KUEHL: Actually, I think this snippet should look like this: for (; !myStack.isEmpty(); myStack.pop()) { // do something to myStack.top() } SHEPPARD: : --- instead of this piece of code: : : try : { : while ( someObj = myStack.pop() ) : { : // do something to the returned object : } : } : catch ( Stack::StackErrors ) {} KUEHL: Again, this use of pop should IMO be avoided. The technique of using an exception to terminate the loop looks strange to me: Not considering that the class would need a conversion to some type which can be promoted to 'bool' (a conversion to some integral type or to a pointer type) to make the loop work (with all involved pitfalls), I would expect that this loop terminates due to exhaustion of the the stack and that it is rather an endless loop. It is IMO more a case for an obfuscated C++ contest than for production code which should use known idioms where possible. SHEPPARD: : Other than the argument that ``try...catch wasn't meant to be used : that way...'' are there efficiency or other reasons to perfer the : first loop over the second one (or vice-versa)? Or is there an even : better method? KUEHL: As discussed in "Inside the C++ Object Model" (S.Lippmann, Addison-Wesley), the exception mechanism involves some overhead, at least if an exception is triggered: Using exceptions intensively for flow control probably involves penalties. This somewhat depends on the technique choosen by the compiler developers but I hope that they choose excpetion handling methods which involve no or only minor overhead if no excpetion is thrown: I use exceptions only for exceptional cases. However, with these methods throwing an exception is normally relative expensive.