I l@ve RuBoard | ![]() ![]() |
Solution![]() To implement the copy constructor and the copy assignment operator, let's use a common helper function, NewCopy, to manage allocating and growing memory. NewCopy takes a pointer to (src) and size of (srcsize) an existing T buffer, and returns a pointer to a new and possibly larger copy of the buffer, passing ownership of the new buffer to the caller. If exceptions are encountered, NewCopy correctly releases all temporary resources and propagates the exception in such a way that nothing is leaked. template<class T> T* NewCopy( const T* src, size_t srcsize, size_t destsize ) { assert( destsize >= srcsize ); T* dest = new T[destsize]; try { copy( src, src+srcsize, dest ); } catch(...) { delete[] dest; // this can't throw throw; // rethrow original exception } return dest; } Let's analyze this one step at a time.
Copy ConstructionWith NewCopy in hand, the Stack copy constructor is easy to write. template<class T> Stack<T>::Stack( const Stack<T>& other ) : v_(NewCopy( other.v_, other.vsize_, other.vsize_ )), vsize_(other.vsize_), vused_(other.vused_) { } The only possible exception is from NewCopy, which manages its own resources. Copy AssignmentNext, we tackle copy assignment. template<class T> Stack<T>& Stack<T>::operator=( const Stack<T>& other ) { if( this != &other ) { T* v_new = NewCopy( other.v_, other.vsize_, other.vsize_ ); delete[] v_; // this can't throw v_ = v_new; // take ownership vsize_ = other.vsize_; vused_ = other.vused_; } return *this; // safe, no copy involved } Again, after the routine weak guard against self-assignment, only the NewCopy call might throw. If it does, we correctly propagate that exception, without affecting the Stack object's state. To the caller, if the assignment throws then the state is unchanged, and if the assignment doesn't throw, then the assignment and all its side effects are successful and complete. What we see here is the following very important exception-safety idiom. Guideline
|
I l@ve RuBoard | ![]() ![]() |