Writing exception safe classes ============================== In the following I'll use the following vocabulary: a) a var_ptr is a smart pointer employing reference counting; it closely simulates variables to strcutured types in Java: when the last var_ptr reference to an object goes out of scope, the object will be deleted (as compared to: marked as due for deletion in Java) b) an auto_ptr is a smart pointer as specified by the C++ standard, that owns the object it is pointing to; only one auto_ptr may own an object - on assignment ownership is transferred! c) an "Exception Atomic" operation is one, that will never be interrupted by a thrown exception. This can be made sure by looking at the exception throw specification or by assuring, only builtin operations are carried out (Note: be aware especially of assignment operators, constructors and user defined conversion operators) d) throw(none) Functions: functions with an empty throw specification, indication, that no exception is ever thrown from such a function. e) "Weak promise": we promise, that after an exception bubbles up from one of our member functions, the object can be safely destroyed. f) "Strong promise": we promise, that after an exception was thrown from one of our members the object is in the state it was BEFOR the function was called. (compare this to Database Manager Transactions) We distinguish 3 cases or points of interest: 1.) Constrcutors 2.) Destructors 3.) Normal members Therefor I derive the 3 Golden Rules: Rule 1) If you have to use free store, prevent memory leaks. Solution a: Initialize all pointer type members to zero first; try to allocate from the free store then; wrap all in a try catch(...) that explicitely deletes all pointer type members Solution b: (preferable) Use auto_ptr or var_ptr instead of builtin pointer types. ("Resource allocation is aquisition") Rule 2) Dont throw in destructors. Never allow an exception to propagate to the caller; Use try catch in EVERY destructor, that contains functions calls /objects that might throw. Use no try catch(...) if only delete or delete[] and throw(none) functions are called -> the implementor of the these objects/functions is then responsible. Rule 3) Try to mimic Transaction / Commit / Rollback behaviour; ensure a destroyable state otherwise. a) Use an intermedediate copy of the status variables and update these. If updates went ok, swap the contents of these with the contents of the object in one "Exception Atomic" operation. (Using operations that don't throw only; e.g. STL swap() ) ; b) If that is not feasible because of time constraints, implement only the weak garantuee: the objects state shall be so, that it can safely be destroyed. (like STL members vector::insert, vector::erase) General Rule: Think about useing try/catch/rethrow in each and every member function. Do not use it actually, but imagine what your work would be to do in a catch(...) clause, to garantuee the weak or strong promise. Only when having determined specifically that its safe to leave it out for a certain member, leave it out. It is safe: if at least the destructor of the object can run successfully. Another possibility: Use var_ptr instead of builtin ptrs. var_ptr cannot be deleted; var_ptr maintains an internal reference count; when it reaches zero, the object pointed to will be deleted (simulates Java variables). This will spare: 1.) Try / Catch in constructor 2.) Try / Catch in reallocating members: Instead of: T *newptr = 0 ; // nox try { // Construct new array newptr = new char[newlen] ; // >x // Initialize new array for( int i=0 ; ix } delete ptr ; ptr = 0 ; // this would onlybe necessary, if the next line // could throw ptr = newptr ; len = newlen ; } catch(...) { delete newptr ; throw ; } say, given ptr is of type car_ptr: var_ptr newptr = new char[newlen] ; for( int i=0 ; ix } ptr = newptr ; // deletes ptr and assigns newptr automatically len = newlen ; the same would work for auto_ptr: (again given ptr is of type car_ptr) auto_ptr newptr = new char[newlen] ; for( int i=0 ; ix } ptr = newptr ; // deletes ptr and assigns newptr automatically len = newlen ; The additional advantage of var_ptr would be the possibility to use it throughout the program to replace all pointer occurences; Generally this will prevent memory leaks during exception handling; furthermore this allows vector<> and other container classes to be used with var_ptr's and memory will be released as necessary, even during applying STL algorithms (see my paper: STL Containers: an Oddysee)