Index of /~TDDD38/exercises/Container_Design/src/Step_4

[ICO]NameLast modifiedSizeDescription

[PARENTDIR]Parent Directory  -  
[TXT]Container.h2015-02-11 09:45 1.3K 
[   ]Container.tcc2015-02-11 09:45 6.6K 
[   ]Makefile2015-02-11 09:45 595  
[TXT]README2015-02-11 09:45 2.9K 
[TXT]container-test.cc2015-02-11 09:45 2.0K 

README Container Design, Step 4
------------------------------------------------------------------------------
In this step we have added operations for inserting, inspecting, and removing
elements, push_back(), back(), and pop(). Here is the copy version of push_back(): 

   template <typename T>
   void 
   Container<T>::
   push_back(const T& x)
   {
      if (size() == capacity())
      {
         resize(x);     // can fail
      }
      else
      {
         *finish_ = x;  // can fail
      }
      ++finish_;
   }

resize() can fail, when allocating new memory, copy x to its place, or copying 
objects from old storage to the storage. 

   template <typename T>
   void
   Container<T>::
   resize_(const T& x)
   {
      size_type old_size = size();
      size_type new_capacity = compute_capacity_();
      T* start = allocate_(new_capacity);
   
      try
      {
         *(start + old_size) = x;
         std::copy(start_, finish_, start);
      }
      catch (...)
      {
         deallocate_(start);
         throw;
      }
   
      delete[] start_;
      start_ = start;
      finish_ = start_ + old_size;
      end_of_storage_ = start_ + new_capacity;
   }

When applying copy semantics it's simple to handle problems, just deallocate the
new storage and keep the current state of the container. allocate_() is fail-safe,
the assignment to x is handled exception neutral, if copy() fails we handle this
is handled in an exception-safe way - resize_() is fail-safe.

Note: When applying move semantics it gets much more complicated, since we then
have to keep track of how many of the objects have been moved when a failure occurs,
to be able to restore the state before starting moving elements.

According to standard conventions, operations such as back() and pop_back() shall
not throw, for example if the container is empty.

   template <typename T>
   const T&
   Container<T>::
   back() const
   {
      auto tmp = finish_;
      --tmp;
      return *tmp;
   }

   template <typename T>
   void
   Container<T>::
   pop_back() noexcept
   {
      --finish_;
      finish_->~T();
   }

Here we don't do anything that should throw - copying a pointer, decrementing a
poiner, returning a reference, invoking a destructor.

Seems that we are still robust!
--------------------------------------------------------------------------------

What are the requirements on the element type T?
------------------------------------------------

- Default constructor, should be exception-safe.

- Non-throwing destructor, to be able to guarantee exception-safety.

- Copy constructor, should be exception-safe.

- Copy assignment operator, should be exception-safe.

- Move constructor and move assignment operator, if available, should not fail.

If an operation throws, it must guarantee that the target object is still a 
valid T. 

------------------------------------------------------------------------------