Index of /~TDDD38/exercises/Container_Design/src/Step_4
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.
------------------------------------------------------------------------------