################################################ # # # ## ## ###### ####### ## ## ## ## ## # # ## ## ## ## ## ### ## ## ## ## # # ## ## ## ## #### ## ## ## ## # # ## ## ###### ###### ## ## ## ## ### # # ## ## ## ## ## #### ## ## ## # # ## ## ## ## ## ## ### ## ## ## # # ####### ###### ####### ## ## ## ## ## # # # ################################################ The following paper was originally published in the Proceedings of the USENIX 1996 Conference on Object-Oriented Technologies (COOTS) Toronto, Ontario, Canada, June 1996. For more information about USENIX Association contact: 1. Phone: 510 528-8649 2. FAX: 510 548-5738 3. Email: office@usenix.org 4. WWW URL: https://www.usenix.org Pattern Languages for Handling C++ Resources in an Exception-Safe Way Harald M. M|ller Siemens AG Austria harald.mueller@siemens.at Abstract Using exception handling in C++ can lead to severe problems with dynamic objects and other resources- dangling pointers, memory leaks etc. By using a small set of patterns (collected into a pattern language "Ex- ception-safe C++ objects"), these problems can be avoided. However, it turns out that these pattern make use of "resource management", which poses a lot of questions in itself. For the solution of these problems, a second, somewhat larger set of patterns (collected into a pattern language "Responsibility management under exception handling") is provided. 1. Introduction Using exception handling in C++ can lead to severe problems with dynamic objects and other resource- dangling pointers, memory leaks etc. A number of ar- ticles have dealt with these problems (e.g. [1], [2], [3], [7]), and some solution patterns have been provid- ed. Here, we collect all these solutions and organize them into a pattern-like way for easy application (and possible criticism). The article is organized as follows: Section 2 pro- vides three patterns for solving fundamental problems of exception handling. The last of these patterns re- quires correct resource management for all resources in a program, which is a non-trivial problem in itself. Therefore, section 3 goes on to provide patterns for a generalization of resource management, called respon- sibility management. Before dealing with "good pat- terns", a bad one is also shown as an example of how not to do it. After that, various patterns for managing responsibilities are provided, depending on the servic- es provided by the environment (e.g., is a garbage col- lector available or not) and on the complexity of how an object might be managed by other objects (e.g. is there always a single manager for each object?). One important case that requires three patterns of its own occurs if the responsibility for a resource is to be passed around among several managers, yet, there must always be exactly one responsible at any time. The patterns dealing with this case are described in section 3.4.2. The patterns in this article are presented in the Alex- andrian form described in [8]; at a few places, a "mo- tivation" section or an "alternative name" section is in- cluded additionally. 2. Exception-safe C++ objects 2.1. Problem space Exception handling changes the nature of algorithms considerably: When an exception is thrown deep in the call stack, it is possible that many functions are suddenly interrupted in the middle of a computation (indeed, this is the rationale for exception handling). This means that those functions will not be able to ful- fill the duty they were designed for, which could mean that data structures are no longer in a usable state. A simple example of such an event is the fol- lowing: Consider a class for storing Gregorian dates: struct GregorianDate { string month; unsigned day; unsigned year; }; Assume that an object of this type stores the date June 30th, 1996. The following sequence of opera- tions tries to change this to July 31st, 1996: day = 31; month = "July"; If an exception is thrown in the assignment to month (e.g. due to lack of memory) and month keeps its original value "June", we suddenly have stored a bad date-"June 31st, 1996". If the date values are used to index a table later on, it might well be that the pro- gram crashes. The problem is most serious with objects that cannot even be deleted after an exception interrupted an oper- ation on them (see the BoundedStack example in sec- tion 2.4.1). The following text presents some patterns that explain how to deal with this sort of problems. The fundamen- tal problem discussed here was presented in [2], the solutions stem from [7]. 2.2. Terminology Resource A resource is a single object or a tightly coupled group of objects that live longer than a single opera- tion (this means that temporary objects are usually not viewed as resources). A resource usually must be created (or "acquired") and released (or "destroyed") by distinct operations. An example of a resource consisting of a tightly cou- pled group of objects is the argv and argc parame- ters to the main() function. Remark: Although resources are the basic sort of thing dealt with in this article, we give only a vague definition of this term. The reason is that it is not the case that something in a program is or is not re- source, but rather that many things can be viewed as resources. For resources that consist of a single C++ object, we will in the following equate resource release and ob- ject destruction. However, there might be resources around that provide different operations for resource release (a simple example are C files, which are re- leased by a call to fclose()). Resource invariant For most resources, not all combinations of states of sub-resources make correct states of the compound re- source. For example, the GregorianDate struct de- fined above might have the value day == 42 month == "" year == -1 However, this is not a valid Gregorian date. The same is true for the state day == 29 month == "February" year == 1995 Obviously, there is a condition restricting the values of the sub-resources so that only those values that "make sense" in the application are possible. We call this condition the "resource invariant". For GregorianDate, this is a condition that con- nects the value of day to the name of the month and the value of year (because of leap years). Moreover, the date must be larger than October 15th, 1582, the day when Pope Gregor introduced the new calendar in (most of) Italy. At many places in this article, we will use a general Stack class template to describe problems and solu- tions to them (the declaration of this template is given in the appendix). A few important parts of the invari- ant of this class are: 7 nelems is not larger than the size of the array pointed to by vec 7 top <= nelems 7 top >= 0 The "State Scale" As mentioned in the introduction, the big problem with exception handling is that the interruption of a computation may leave some resources in a state not acceptable for following operations. However, the "badness" of the state may be of a varying degree- e.g. it might be so bad that the resource cannot even be released; or it could merely have retained its previ- ous state, which means that it is no longer correct, but certainly still usable. The following defines a partion- ing of the possible states into different categories; the main discrimination is based on the resource invariant introduced above. After an operation finishes, the state of a resource ac- cessed and possibly modified by this operation may be in one of the following state classes: 7 Consistent state - any state fulfilling the resource invariant. 7 Correct state - as it is defined by the specifica- tion of the operation. 7 Incorrect state. 7 Same state - the same state as before the operation (if this is not the correct state). 7 Any consistent state - any other consistent state (e.g. a state like after resource cre- ation). 7 Inconsistent state - a state contradicting the class invariant. 7 Shut-down state - an inconsistent state where at least resource release is possible. 7 Very bad state - a state where potentially no operation (not even a resource release) is possi- ble. Resources in an inconsistent state are also called "damaged resources" or "bad resources" in the follow- ing; resources in a consistent state are also called "good resources" or "resources in a good state". 2.4. Patterns 2.4.1. "Ensure Shut-down-ability" 7 Problem An exception might interrupt a function modifying some resource at such a moment that the resource is no longer in a consistent state afterwards. In general, the inconsistent state might be a very bad state, which means that the resource cannot even be destroyed af- terwards. 7 Forces 1. It is necessary that no resource enters a very bad state, as every resource must eventually be released. 2. Indeed, it is preferable that all resources are as high as possible on the state scale. 3. On the other hand, it is very complicated to write all modification functions in such a way that they leave all resources only in consistent states (see e.g. the GregorianDate example in the introduction). 4. At least some operations must be possible on re- sources even if an exception was thrown during a modification. 7 Solution Design all resources and all modification functions on them in such a way that the resources never enter a very bad state. Heuristic A: Do not have a value necessary for de- struction depend on two or more variables ("do not do computations in destructors"). Heuristic B: Do not store values other than those a de- structor expects in an object's attributes (e.g. do not use class members as "temporaries"). 7 Examples The GregorianDate struct is an example of a re- source that is designed according to this principle: The (trivial) destructor will work regardless of the val- ues that have been assigned to the member variables. An example of a class violating this rule is the BoundedStack class declared the appendix-a stack class with a maximum number of elements. Its mem- ber variable current_top always points one ele- ment above the top element, and current_size is the number of pushed values. The element counter current_top is implemented by a class Bounded- Int that can only represent a bounded interval of inte- gers. Operations trying to assign a value outside this interval throw an exception. The destructor of a BoundedStack object simply computes the base of the allocated array and then de- letes it: template BoundedStack::~BoundedStack() throw () { delete [] (current_top - current_size); } -however, this contradicts Heuristic A given above. The problems arising from this design are explained in the paragraph on "Design rationales". 7 Force resolution This pattern steers an intermediate way between items 1. and 2. in the forces' list on the one hand, and item 3. on the other. It might appear that this solution is somewhat weak- one would expect that using exception handling would yield more powerful error handling policies that would also allow to repair damaged resources somehow. However, in most real applications throw- ing an exception means that all resources modified by a function interrupted by the exception can no longer be used. If the resource stores its information non-re- dundantly-which is the case for the vast majority of data structures-, it is not possible to reconstruct the correct result, anyway. So shutting down the resource and others that depend on it is typically the only pos- sible solution. 7 Design rationale It is important to notice that by this pattern, we explic- itly allow resources to be in bad states. One might wonder whether this is really necessary-after all, isn't it possible to keep resources in some good state all the time, e.g. by always returning to the (necessari- ly good) state from before the exception? Alas, it turns out that this is equivalent to a complete transac- tion system with full rollback capabilities-certainly not the right approach for the small resources that are customarily needed in a C++ program. For more infor- mation, see [7]. Heuristic A: For two or more variables, any modifica- tion must occur as a sequence of modification steps. If an exception is thrown by one of these steps, the compound resource consisting of all the variables might no longer be in a consistent state, hence a val- ue computed from some of the variables might be gar- bage. An example for this is the following implemen- tation of BoundedStack::push(): template void BoundedStack::push(const T& e) // throw (overflow_error, ... T&=const T& ) { ++current_size; *current_top++ = e; } If the increment of current_size throws an excep- tion (which is possible if we try to push more ele- ments than the stack can hold), we end up with an in- cremented current_size value, but current_top has not changed, and hence the destructor will call delete with an invalid argument. Heuristic B simply addresses a common misuse of member variables. 7 Consequence A consequence (or rather the consequence) of this pat- tern is that resources might be in bad states, i.e., states where no operation except release is possible. This means that other parts of the system that might access the resource in the future must get notice of the state of affairs so that they can avoid these access- es. That this should be done explicitly is codified in the next pattern. 2.4.2. "Hide damaged resources till destruction" 7 Problem A modification of a resource might fail. Use of "En- sure Shut-down-ability" means that the resource can- not be used afterwards, but only destroyed. In gener- al, every such resource is part of some other, larger re- source. The important question now is: When, and how, is it possible to continue using the larger re- source? 7 Forces 1. Keeping all sub-resources of the larger resource al- ways usable is not feasible (see previous pattern). 2. Throwing away each resource with a single dam- aged sub-resource might result in shut-down of func- tionalities that could still be provided. 3. It is very dangerous to rely on the promise of other program parts that the damaged sub-resource will not be accessed until destruction of the whole resource. 7 Solution a. If the sub-resource's value is part of the resource's invariant, the complete resource must be viewed as damaged and therefore abandoned (if the sub-resourc- e's value is not consistent in itself, the condition of the larger resource cannot possibly be true). b. Also if the exception thrown might have left values from the invariant in an inconsistent state (although each single value is still usable), the complete re- source must be viewed as damaged and abandoned. c. If the sub-resource's value is not part of the re- source's invariant, explicitly hide the damaged sub-re- sources from all further accesses except those in the destructor of the resource. 7 Examples Case a. Assume that we repair the BoundedStack class shown in section 2.4.1, e.g. by providing a sepa- rate data member pointing to the base of the allocated array. Now look at the push() operation: If an excep- tion is thrown by the increment of current_top, we must assume that this sub-resource is damaged. Hence, we must abandon the complete Bounded- Stack object. Case b. The push operation for the stack class must enlarge the internal array if it is full. This can be ac- complished e.g. as follows: template void Stack::push(const T& e) // throw (bad_alloc, ...T(), ...T&=const T&) { if (top == nelems) { nelems *= 2; T *new_buf = new T[nelems]; for (unsigned i=0; i void Stack::push(const T& e) // throw (bad_alloc, ...T(), ...T&=const T&) { // ... vec[top++] = e; } If the assignment fails, the stack is no longer usable afterwards: The top element may be in a bad state, hence no operation except destruction is possible. However, the value of the top element certainly is not relevant for the functioning of the stack, hence it will not appear in the invariant of a stack resource. Thus, it should be possible to continue the use of the Stack object. To this end, we must hide the damaged element from further use. A simple remedy is to reduce nelems to top so that all methods (except the destructor!) now believe that there are fewer elements allocated (watch out for the increment of top; it must now be post- poned until it is clear that the assignment did work): void Stack::push(const T& e) // throw (bad_alloc, ...T(), ...T&=const T&) { // ... try { vec[top] = e; top++; } catch (...) { nelems = top; throw; } } This course of action might be worthwhile for exam- ple when the stack is used for recalling commands that a user typed at some earlier time. For such an ap- plication, it might make more sense to lose some in- put than to shut down the complete user interface. 7 Force resolution The damaged sub-resources are no longer available for any functionality the larger resource provides. However, all functionalities not using the damaged parts are still available. 7 Consequences It is a necessity to design explicitly the various "re- stricted functionalities" of a resource, i.e., which oper- ations are still available after various sub-resources went bad. 2.4.3. "Resource management is necessary" 7 Motivation The previous pattern showed what to do if part of a resource entered an inconsistent state. However, cer- tain resources must never even go into an incorrect state, as we cannot afford to live without them. For these resources, the sequence of operations manipulat- ing them must always be correct. The most outstanding example of such a resource is the dynamic heap. Here, we require for example that for each successful new operation, a corresponding delete must be executed somewhen later. As excep- tions may interrupt statement sequences quite forceful- ly, it is not immediately clear how this requirement might be fulfilled. If one operates the heap naively in the presence of exception handling, throwing an ex- ception might easily interrupt sequences manipulating the heap, thereby leading to memory leaks and/or dou- ble deletes (see [2]). The pattern "Hide damaged resources till destruc- tion," which promises to deal with damaged parts of a resource, does not work for the global heap: It would require us to "hide" "damaged parts" of the heap (e.g. where a double delete or a memory leak has oc- curred) from further access. However, this is definite- ly not possible in C++ (and would probably not make much sense anyway, as the available space in the cor- rectly maintained part of the heap would get smaller and smaller until nothing was left). The Stack::push() method explained in the previ- ous pattern already showed how code that is correct without exception handling will be incorrect when ex- ceptions might get thrown. It contains a section for re- placing the internal buffer by a larger one: if (top == nelems) { nelems *= 2; T *new_buf = new T[nelems]; (a) for (unsigned i=0; i that can be used as a resource management class for a single heap object of class T. However, it is lacking a similar class for ar- rays allocated on the heap (this would only have to call delete [] instead of delete in the destruc- tor). For a possible implementation of such a class, see the auto_ptr_array class template in the ap- pendix. 7 Remark Some other languages provide direct language means for writing statements that are guaranteed to be exe- cuted even if an exception is thrown. Examples in- clude the valueNowOrOnUnwindDo: method in Smalltalk or the finally clause of Java. 3.4.2. "Sequence of exclusive responsibility holders" 7 Solution There is always exactly one responsibility holder for resource release. However, by passing around the re- sponsibility among many responsibility holders, it is possible to keep the resource alive longer than the cre- ating block is active. 7 Force resolution This solution is a generalization of the "Resource Re- lease is Object Destruction" pattern. It keeps its effi- ciency and determinism, however, the code complexi- ty is higher than with all other patterns. Moreover, it produces a lot of problems of its own (these will be dealt with in following sections). The main problem of this solution is that explicit re- sponsibility transfers are necessary. This happens for instance if the creating block transfers its responsibili- ty for some allocated memory to an object before it goes out of scope. We will use these responsibility transfers to ensure that all resources get released ex- actly once even in the presence of exceptions. Howev- er, this makes it necessary that the transfers them- selves do not throw an exception, i.e., they must be secure-otherwise, we could no longer be sure where the responsibilities lie after a failed transfer. In almost all cases, this can be ascertained by moving around only some kind of reference value, not the resources themselves. Such values might be pointers, integral values or certain types of iterators that may be secure- ly copied. This solution will probably be the standard solution used in C++ programs, although in some respects it might be considered the most difficult one. It certain- ly places the highest burden among all solutions on the programmer. 3.4.2.1. "Responsibility transfer is swap" 7 Problem If a responsibility for a resource is passed from a re- sponsibility holder A to another responsibility holder B, it might be the case that B already is responsible for some other resource. Simply discarding this re- sponsibility is obviously not possible, as we postulat- ed that there must always remain exactly one responsi- bility holder. What are we going to do with the "su- perfluous responsibility"? 7 Forces Besides the ubiquitous correctness requirement, there are two more forces on solutions for this problem: 1. It should not be required to declare and handle more responsibility holders than absolutely necessary; and 2. the code complexity should be small, as this pat- tern is only a stepping stone for other patterns. 7 Solution Swap the responsibilities between A and B. 7 Examples The following is an example where a resource manag- er called perm takes over the responsibility for an ob- ject of class X from a local resource manager. The swap_with() method of class auto_ptr (which is not defined in the proposed C++ standard) exchanges the internal pointers of the two objects. At first we show the wrong solution to this problem: auto_ptr perm; // ... { auto_ptr temp; temp.reset(new X); // ... perm.reset(temp.release()); // wrong: perm might already be // responsible for some resource! } The correct solution exchanges the responsibilities of perm and temp: auto_ptr perm; // ... { auto_ptr temp; temp.reset(new X); // ... perm.swap_with(temp); // temp's destructor can now take // care of the resource for which // perm was responsible before, if // there was any. } 7 Force resolution For once, the forces can be perfectly resolved: The pattern is obviously correct; it certainly needs the min- imal number of responsibility holders; and the code complexity is really small. 7 Remark Swapping responsibilities does not require that one of the resources must be released afterwards. It is per- fectly legal to swap two "active" resources that are both used after the swap. The following two patterns describe two frequent ap- plications of "Responsibility transfer is swap", name- ly resource replacement and acquisition of a new re- source. They refine a group of "Local Ownership" pat- terns given in [6] by making them exception-safe. 3.4.2.2. "Resource replacement" 7 Problem Replace an old resource under object responsibility by a new one. This problem occurs in many basic opera- tions, e.g. assignment (where the old member values must be replaced by new ones), in containers that au- tomatically adjust their internal storage (e.g. a string class that enlarges its buffer if characters are added; or our running example, the Stack class template), and elsewhere. 7 Forces 1. The resource replacement must be correct, i.e., for the old resource as well as for the new one there must always be exactly one resource manager. 2. The code complexity should be as small as possible. 7 Solution (a+) Allocate a local resource by creating a resource management object on the stack. (b+) The local resource is used (usually initialized) as necessary. (c) The responsibilities for the object's resource and the local resource are swapped. (d) The destructor of the resource management object releases the now local resource (the former object re- source). 7 Examples The following two examples show the application of this pattern to the assignment and the push operation of the stack class template. template Stack& Stack::operator=(const Stack& s) // throw (bad_alloc, ...T()) { if (this != &s) { auto_ptr_array new_buf(nelems=s.nelems); (a+) for (unsigned i=0; i void Stack::push(const T& e) // throw (bad_alloc, ...T(), ...T&=const T&) { if (top == nelems) { nelems *= 2; auto_ptr_array new_buf(nelems); (a+) for (unsigned i=0; i Stack::Stack() // throw (bad_alloc, ...T()) : vec(0) (a+) { auto_ptr_array buf(nelems=10); (b+) // (c+ not necessary here) buf.swap_with(vec); (d) /* destructor of buf */ (e) } 7 Force resolution See "Resource Replacement" pattern. 7 Design rationale One could design a separate pattern for acquisition of new resources that does not need the "arbitrary re- source" from step (a). However, the pattern above has the advantage that it inherits its correctness from the "Resource replacement" pattern. Moreover, this pattern fits well with the C++ style rule that member variables should be set in the initial- izer list of a constructor: As shown above, the mem- ber gets the responsibility for a trivial resource in the initializer list, which is then replaced by the real re- source in the constructor's body. The patterns described in sections 3.4.1. and 3.4.2. as- sume that there is only one responsibility holder. This has the advantage that it is always clear who will car- ry out the required operation, but it has the disadvan- tage that passing around responsibilities is either not possible ("Resource Release is Object Destruction") or quite complex ("Sequence of exclusive responsibili- ty holders"), or that the underlying system must be quite powerful ("Garbage Collector"). The following patterns describe alternative approach- es to the responsibility management problem by allow- ing more than one responsibility holder. From the viewpoint of exception handling, the fundamental problem of correct resource release thereby vanishes (almost): We simply give the responsibility to release a resource to a lot of responsibility holders; if the ac- tivities of one of these are interrupted by an excep- tion, we can nevertheless be quite certain that some other responsibility holder will eventually carry out the release operation. 3.4.3. "Reference counting" 7 Solution Many responsibility holders take over the job to re- lease a resource. The release happens when the last re- sponsibility holder declares that it wants to do its duty. For details on this well-known pattern, see e.g. [9] or [6]. It is also mentioned as a special case of the "Proxy" pattern in [10]. 3.4.4. "Idempotent operations" 7 Solution Make resource release an idempotent operation, i.e. an operation that has no effect after the first execu- tion. Thus, many responsibility holders may exist at one time, each of whom might carry out the release operation. 7 Examples An application has a central log file that is to be closed either if the user explicitly requires it, or when the application finishes. For this, a central log-file- handler could provide a function Log_file_handler::close_log_file() { delete p_log_file; p_log_file = 0; } The duty to call this function is on the one hand giv- en to a central application shut-down process. Howev- er, other processes-e.g. the afore-mentioned user command for explicitly closing the logfile-may equally call this function without giving notice to the shut-down process, as a repeated execution does no harm. 7 Force resolution In contrast to the reference counting scheme, with "Idempotent operations," the first responsibility hold- er that wants to carry out its duty will accomplish the resource release. It depends on the application wheth- er this is possible or not. Moreover, it must be still ascertained that at least one responsibility holder carries out its duty (as with refer- ence counting). The last problem can be overcome by putting the re- sponsibility to a single central responsibility holder which swears to fulfilling its duties-see "Garbage Collector." We've come a full circle. 4. Conclusion This article tried to cast a set of rules necessary for writing exception-safe code into patterns form. Wide- spread use of exception handling, especially in larger systems, is not yet common in the C++ community, therefore it is hoped that patterns of this kind help to deal with the problems inherent in this complex fea- ture. However, there are some open points that have to be addressed in the future: At some places, implicit assumptions are not yet clear- ly spelled out (e.g. that it is assumed that responsibili- ty management objects are not class members, but only local variables); at other places, explicit condi- tions could be weakened or left away (e.g. there seem to be some meta-patterns behind the patterns present- ed in 3.5.2). Moreover, as always, the patterns certtainly need re- finement by potential and actual users. The patterns shown in section 3 are useful for resourc- es that are designed according to "Ensure Shut-down- ability", i.e., it is not necessary that the resource itself is in some good state after an exception occurred. For resources that must have a quality as high as e.g. the global heap, i.e., where it must be possible to contin- ue using them after an exception interrupted some op- eration, more powerful patterns are necessary that guard all modifications of all variables in a piece of code. An introduction to concepts for solving this problem can be found in [7]; shaping them into pat- tern form might be the contents of a future article. 5. References [1] Stroustrup, B. The Design and Evolution of C++. Addison-Wesley, Reading, MA, 1990. [2] Cargill, T. Exception handling: A false sense of se- curity. C++ Report 6(9):21-24, Nov.-Dec. 1994. [3] Carroll, M. and M. A. Ellis. Tradeoffs of excep- tions. C++ Report 7(3):12-16, March-April 1995. [4] Ellis, M. A. and B. Stroustrup. The Annotated C++ Reference Manual. Addison-Wesley, Reading, MA, 1990. [5] Working Paper for the C++ standard, April 1995. [6] Cargill, T. Localized Ownership: Managing Dy- namic Objects in C++. Proceedings of the 2nd Pattern Languages of Programming Conference, September, 1995. [7] M|ller, H.M. 10 Rules for Handling Exception Handling Successfully, C++ Report 8(1):22-36, Jan. 1996. [8] Coplien, J.O. Software Design Patterns: Common Questions and Answers. URL: ftp://st.cs.uiuc.edu/pub/ patterns/papers/PatQandA.ps. [9] Coplien, J.O. Advanced C++ Programming Styles and Idioms. Addison-Wesley, Reading, MA, 1992. [10] Gamma, E. et al. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, Reading, MA, 1995. [11] Boehm, H.J. Garbage Collection in an Uncooper- ative Environment. Software Practice and Experience 18:807-820, Sept. 1988. [12] Boehm, H.J. A garbage collector for C and C++. URL: ftp://parcftp.xerox.com/pub/gc/gc.html. A. Appendix A.1. Declaration of GregorianDate struct GregorianDate { string month; unsigned day; unsigned year; }; A.2. Declaration of Stack template class Stack { public: class XPopEmpty:public domain_error { public: XPopEmpty(const string& info) : domain_error(info) { } }; Stack(); // throw (bad_alloc, ...T() ) Stack(const Stack&); // throw (bad_alloc,...T(),...T&=const T&) ~Stack() throw (); Stack& operator=(const Stack&); // throw (bad_alloc,...T(),...T&=const T&) unsigned count() const throw() { return top; } void push(const T& e); // throw (bad_alloc,...T(),...T&=const T&) T pop(); // throw (XPopEmpty,...T(const T&) ) private: unsigned nelems; unsigned top; T* vec; }; A.3. Declaration of BoundedInt and BoundedStack class BoundedInt { public: BoundedInt(int bottom, int top) throw () : bottom_(bottom) , top_(top) { } BoundedInt operator++() throw (overflow_error) { if (++value_ >= top_) throw overflow_error(...); return *this; } operator int() throw() { return value_; } // ... private: const int bottom_, top_; int value_; }; template class BoundedStack { public: BoundedStack(unsigned maxsize) // throw (bad_alloc, ...T() ) : current_size(0, maxsize) , current_top (new T[maxsize]) { } ~BoundedStack() throw (); void push(const T& e); // throw (overflow_error,... T&=const T& ) // ... private: BoundedInt current_size; T* current_top; }; A.4. Declaration of auto_ptr_array template class auto_ptr_array { public: auto_ptr_array(size_t n) // throw (bad_alloc, ...T()) : ptr_(new T[n]) { } ~auto_ptr_array() throw () { delete [] ptr_; } T& operator[](unsigned i) throw () { return ptr_[i]; } T* release() throw () { T* tmp = ptr_; ptr_ = 0; return tmp; } void swap_with(T*& q) throw () { T* tmp = ptr_; ptr_ = q; q = tmp; } private: T* ptr_; };