Having already attempted an implementation of the std::vector
class here, I decided to take the comments on board and also do some new bits myself (mainly the algorithms for allocation and insert).
I have a commented version, but it exceeded the character limit so I had to add the non-commented version. However, I can provide specific commented functions if you would like in the comment section.
The main reason for doing this was to check that I am doing everything safely, which I am more confident about this time, as I have never had errors during the process, while in my previous implementation, I had some errors when testing it and had to fix those a few times, but also to check the efficiency of the code and if I could make some algorithms/functions better.
# ifndef __VECTOR_H__
# define __VECTOR_H__
# include <memory>
# include <algorithm>
template<typename T,
typename A>
class vector;
template<typename B,
typename R>
class vector_iterator;
template<typename I>
class vector_reverse_iterator;
template<typename A>
class vector_base
{
public:
typedef vector_base<A> base_type;
typedef typename A allocator_type;
typedef typename A::pointer pointer;
friend class vector<typename A::value_type, A>;
friend class vector_iterator<base_type, typename A::reference>;
friend class vector_iterator<base_type, typename A::const_reference>;
friend class vector_reverse_iterator<vector_iterator<base_type, typename A::reference> >;
friend class vector_reverse_iterator<vector_iterator<base_type, typename A::const_reference> >;
private:
vector_base(allocator_type const &al)
: ms_begin(pointer()),
s_end(pointer()),
m_end(pointer()),
alloc(al)
{
}
~vector_base()
{
}
base_type *get_base()
{
return (this);
}
pointer ms_begin, s_end, m_end;
allocator_type alloc;
};
# ifndef VECTOR_ITERATOR_CHECK_LEVEL
# define VECTOR_ITERATOR_CHECK_LEVEL 1
# endif
template<typename B,
typename R = typename B::allocator_type::reference>
class vector_iterator
{
public:
typedef vector_iterator<B, R> this_t;
typedef B base_type;
typedef typename B::allocator_type::pointer pointer;
typedef typename R reference;
typedef typename B::allocator_type::const_reference const_reference;
typedef typename B::allocator_type::value_type value_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef std::random_access_iterator_tag iterator_category;
friend class vector<value_type, typename B::allocator_type>;
friend class vector_reverse_iterator<this_t>;
friend class vector_reverse_iterator<vector_iterator<B, const_reference> >;
friend class vector_iterator<B, const_reference>;
vector_iterator(base_type *b, pointer p)
: base(b),
ptr(p)
{
}
template<typename R = reference>
vector_iterator(vector_iterator<B, R> const &rhs)
: base(rhs.base),
ptr(rhs.ptr)
{
}
~vector_iterator()
{
}
this_t &operator=(this_t const &rhs)
{
base = rhs.base;
ptr = rhs.ptr;
return (*this);
}
this_t &operator++()
{
++ptr;
return (*this);
}
this_t operator++(int)
{
this_t temp(*this);
++ptr;
return (temp);
}
this_t &operator--()
{
--ptr;
return (*this);
}
this_t operator--(int)
{
this_t temp(*this);
--ptr;
return (temp);
}
this_t operator+(size_type offset) const
{
return (this_t(base, ptr + offset));
}
this_t operator-(size_type offset) const
{
return (this_t(base, ptr - offset));
}
difference_type operator-(this_t const &rhs) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_compatible(rhs);
# endif
return (ptr - rhs.ptr);
}
this_t &operator+=(size_type offset)
{
ptr += offset;
return (*this);
}
this_t &operator-=(size_type offset)
{
ptr -= offset;
return (*this);
}
reference operator*() const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_validity(*this, ptr_in_seq);
# endif
return (*ptr);
}
reference operator[](size_type offset) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_validity(this_t(base, ptr + offset), ptr_in_seq);
# endif
return (*(ptr + offset));
}
pointer operator->() const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_validity(*this, ptr_in_seq);
# endif
return (ptr);
}
bool operator==(this_t const &rhs) const
{
return (ptr == rhs.ptr);
}
bool operator!=(this_t const &rhs) const
{
return (!((*this) == rhs));
}
bool operator<(this_t const &rhs) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_compatible(rhs);
# endif
return (ptr < rhs.ptr);
}
bool operator>(this_t const &rhs) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_compatible(rhs);
# endif
return (ptr > rhs.ptr);
}
bool operator<=(this_t const &rhs) const
{
return (!(ptr > rhs.ptr));
}
bool operator>=(this_t const &rhs) const
{
return (!(ptr < rhs.ptr));
}
private:
pointer ptr;
base_type *base;
static void check_validity(this_t const &it, bool (*test)(this_t const &))
{
if (!test(it))
{
throw std::exception("iterator out of range");
}
}
void check_compatible(this_t const &it) const
{
if (it.base != base)
{
throw std::exception("iterators incompatible (not in same container)");
}
}
static bool ptr_in_seq(this_t const &it)
{
return (it.ptr >= it.base->ms_begin &&
it.ptr < it.base->s_end);
}
static bool ptr_in_seq_or_end(this_t const &it)
{
return (it.ptr >= it.base->ms_begin &&
it.ptr <= it.base->s_end);
}
};
template<typename I>
class vector_reverse_iterator
{
public:
typedef vector_reverse_iterator<I> this_t;
typedef typename I::base_type base_type;
typedef typename I::pointer pointer;
typedef typename I::reference reference;
typedef typename I::const_reference const_reference;
typedef typename I::size_type size_type;
typedef typename I::difference_type difference_type;
friend class vector_reverse_iterator<vector_iterator<base_type, const_reference> >;
vector_reverse_iterator(I const &it)
: base(it.base)
{
size_type from_begin = (it.ptr - it.base->ms_begin) + 1;
ptr = (it.base->s_end - from_begin);
}
template<typename R = reference>
vector_reverse_iterator(vector_reverse_iterator<
vector_iterator<base_type, R> > const &rhs)
: base(rhs.base),
ptr(rhs.ptr)
{
}
~vector_reverse_iterator()
{
}
this_t &operator=(this_t const &rhs)
{
base = rhs.base;
ptr = rhs.ptr;
return (*this);
}
this_t &operator++()
{
--ptr;
return (*this);
}
this_t operator++(int)
{
this_t temp(*this);
--ptr;
return (temp);
}
this_t &operator--()
{
++ptr;
return (*this);
}
this_t operator--(int)
{
this_t temp(*this);
++ptr;
return (temp);
}
this_t operator+(size_type offset) const
{
return (this_t(base, ptr - offset));
}
this_t operator-(size_type offset) const
{
return (this_t(base, ptr + offset));
}
difference_type operator-(this_t const &rhs) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_compatible(rhs);
# endif
return (rhs.ptr - ptr);
}
this_t &operator+=(size_type offset)
{
ptr -= offset;
return (*this);
}
this_t &operator-=(size_type offset)
{
ptr += offset;
return (*this);
}
reference operator*() const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_validity(*this, ptr_in_seq);
# endif
return (*ptr);
}
reference operator[](size_type offset) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_validity(this_t(base, ptr - offset), ptr_in_seq);
# endif
return (*(ptr + offset));
}
pointer operator->() const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_validity(*this, ptr_in_seq);
# endif
return (ptr);
}
bool operator==(this_t const &rhs) const
{
return (ptr == rhs.ptr);
}
bool operator!=(this_t const &rhs) const
{
return (!((*this) == rhs));
}
bool operator<(this_t const &rhs) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_compatible(rhs);
# endif
return (ptr > rhs.ptr);
}
bool operator>(this_t const &rhs) const
{
# if VECTOR_ITERATOR_CHECK_LEVEL >= 1
check_compatible(rhs);
# endif
return (ptr < rhs.ptr);
}
bool operator<=(this_t const &rhs) const
{
return (!(ptr < rhs.ptr));
}
bool operator>=(this_t const &rhs) const
{
return (!(ptr > rhs.ptr));
}
private:
pointer ptr;
base_type *base;
static void check_validity(this_t const &it, bool (*test)(this_t const &))
{
if (!test(it))
{
throw std::exception("iterator out of range");
}
}
void check_compatible(this_t const &it) const
{
if (it.base != base)
{
throw std::exception("iterators incompatible (not in same container)");
}
}
static bool ptr_in_seq(this_t const &it)
{
return (it.ptr > (it.base->ms_begin - 1) &&
it.ptr <= (it.base->s_end - 1));
}
static bool ptr_in_seq_or_end(this_t const &it)
{
return (it.ptr >= (it.base->ms_begin - 1) &&
it.ptr <= (it.base->s_end - 1));
}
};
template<typename T,
typename A = std::allocator<T> >
class vector
: public vector_base<A>
{
public:
typedef typename A::value_type value_type;
typedef typename A::const_pointer const_pointer;
typedef typename A::reference reference;
typedef typename A::const_reference const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef vector_iterator<base_type> iterator;
typedef vector_iterator<base_type, const_reference> const_iterator;
typedef vector_reverse_iterator<iterator> reverse_iterator;
typedef vector_reverse_iterator<const_iterator> const_reverse_iterator;
typedef vector<T, A> this_t;
explicit vector(allocator_type const &al = allocator_type())
: base_type(al)
{
}
explicit vector(size_type count, const_reference value = value_type(),
allocator_type const &al = allocator_type())
: base_type(al)
{
assign(count, value);
}
template<typename InIt>
vector(typename std::enable_if<!std::is_integral<InIt>::value, InIt> first,
typename std::enable_if<!std::is_integral<InIt>::value, InIt> last,
allocator_type const &al = allocator_type())
: base_type(al)
{
assign(first, last);
}
vector(this_t const &rhs)
: base_type(rhs.alloc)
{
if (&rhs != this)
{
assign(rhs.ms_begin, rhs.s_end);
}
}
void assign(size_type count, value_type const &value = value_type())
{
if (is_unconstructed())
{
allocate(count * 1.5);
}
else
{
if (count > capacity())
{
reallocate(count * 1.5);
}
wipe_values();
}
this->s_end = std::uninitialized_fill_n(
this->ms_begin, count, value);
}
template<typename InIt>
typename std::enable_if<!std::is_integral<InIt>::value, void>::type assign(InIt first, InIt last)
{
difference_type count = (last - first);
if (is_unconstructed())
{
allocate(count * 1.5);
}
else
{
if (count > capacity())
{
reallocate(count * 1.5);
}
wipe_values();
}
this->s_end = std::uninitialized_copy(
first, last, this->ms_begin);
}
this_t &operator=(this_t const &rhs)
{
if (&rhs != this)
{
assign(rhs.ms_begin, rhs.s_end);
}
return (*this);
}
iterator erase(iterator where)
{
return (erase(where, where + 1));
}
iterator erase(iterator first, iterator last)
{
iterator::check_validity(first, iterator::ptr_in_seq);
iterator::check_validity(last, iterator::ptr_in_seq_or_end);
difference_type diff = (last - first);
size_type fpos = (first.ptr - this->ms_begin),
lpos = (last.ptr - this->ms_begin);
std::rotate(this->ms_begin + fpos, this->ms_begin + lpos, this->s_end);
while (diff--)
{
this->alloc.destroy(--this->s_end);
}
return (iterator(this->get_base(), this->ms_begin + (lpos - (last - first))));
}
void insert(iterator where, const value_type &value = value_type())
{
insert(where, 1, value);
}
void insert(iterator where, unsigned count, const value_type &value = value_type())
{
iterator::check_validity(where, iterator::ptr_in_seq_or_end);
size_type wpos = (where.ptr - this->ms_begin);
if (is_unconstructed())
{
allocate(count * 1.5);
}
else
{
if (size() + count > capacity())
{
reallocate((size() + count) * 1.5);
}
}
size_type c2 = count;
while (c2--)
{
this->alloc.construct(this->s_end++, value);
}
std::rotate(this->ms_begin + wpos, this->s_end - count, this->s_end);
}
template<typename InIt>
typename std::enable_if<!std::is_integral<InIt>::value, void>::type insert(iterator where, InIt first, InIt last)
{
iterator::check_validity(where, iterator::ptr_in_seq_or_end);
size_type wpos = (where.ptr - this->ms_begin);
difference_type diff = (last - first);
if (is_unconstructed())
{
allocate(diff * 1.5);
}
else
{
if (size() + diff > capacity())
{
reallocate((size() + diff) * 1.5);
}
}
while (first != last)
{
this->alloc.construct(this->s_end++, first++);
}
std::rotate(this->ms_begin + wpos, this->s_end - diff, this->s_end);
}
void push_back(value_type const &value)
{
insert(end(), value);
}
void pop_back()
{
erase(end() - 1);
}
void clear()
{
erase(begin(), end());
}
iterator begin()
{
return (iterator(this->get_base(), this->ms_begin));
}
iterator end()
{
return (iterator(this->get_base(), this->s_end));
}
const_iterator begin() const
{
return (const_iterator(this->get_base(), this->ms_begin));
}
const_iterator end() const
{
return (const_iterator(this->get_base(), this->s_end));
}
reverse_iterator rbegin()
{
return (reverse_iterator(begin()));
}
reverse_iterator rend()
{
return (reverse_iterator(end()));
}
const_reverse_iterator rbegin() const
{
return (const_reverse_iterator(begin()));
}
const_reverse_iterator rend() const
{
return (const_reverse_iterator(end()));
}
reference operator[](size_type offset)
{
return (*(this->ms_begin + offset));
}
const_reference operator[](size_type offset) const
{
return (*(this->ms_begin + offset));
}
reference front()
{
return (*this->ms_begin);
}
const_reference front() const
{
return (*this->ms_begin);
}
reference back()
{
return (*(this->s_end - 1));
}
const_reference back() const
{
return (*(this->s_end - 1));
}
reference at(size_type offset)
{
if (offset >= size())
{
throw std::exception("offset out of bounds");
}
return (*(this->ms_begin + offset));
}
const_reference at(size_type offset) const
{
if (offset >= size())
{
throw std::exception("offset out of bounds");
}
return (*(this->ms_begin + offset));
}
void reserve(size_type count)
{
if (count > capacity())
{
reallocate(count);
}
}
void resize(size_type count, value_type value = value_type())
{
if (count < size())
{
erase(begin() + count, end());
}
else
{
reserve(count);
insert(end(), count - size(), value);
}
}
void swap(this_t &rhs)
{
base_type b = *(this->get_base());
*(this->get_base()) = *(rhs.get_base());
*(rhs.get_base()) = b;
}
size_type size() const
{
return (this->s_end - this->ms_begin);
}
size_type capacity() const
{
return (this->m_end - this->ms_begin);
}
allocator_type get_allocator() const
{
return (this->alloc);
}
bool empty() const
{
return (this->ms_begin == this->s_end);
}
private:
void allocate(size_type count)
{
if (count > this->alloc.max_size())
{
throw std::exception("unable to allocate memory");
}
this->ms_begin = this->alloc.allocate(count);
this->s_end = this->ms_begin;
this->m_end = this->ms_begin + count;
}
void reallocate(size_type count)
{
if (count > this->alloc.max_size())
{
throw std::exception("unable to allocate memory");
}
pointer nbegin = this->alloc.allocate(count);
std::uninitialized_copy(this->ms_begin, this->s_end, nbegin);
size_type sz = size();
wipe_all();
this->ms_begin = nbegin;
this->s_end = nbegin + sz;
this->m_end = nbegin + count;
}
void wipe_values()
{
if (!is_unconstructed())
{
size_type sz = size();
while (this->ms_begin != this->s_end)
{
this->alloc.destroy(this->ms_begin++);
}
this->ms_begin -= sz;
}
}
void wipe_all()
{
if (!is_unconstructed())
{
wipe_values();
this->alloc.deallocate(this->ms_begin, capacity());
this->ms_begin = pointer();
this->s_end = pointer();
this->m_end = pointer();
}
}
bool is_unconstructed() const
{
return (this->ms_begin == pointer());
}
};
# endif