I decided to implement the std::vector
class in C++, and was wondering if I had done everything correctly. When I mean 'correctly', I mean that the code is efficiently written and done properly. I have tested all the functions using various tests and they seem to output correct results.
I have not included every function in the std
class: the main ones that I have not included are assign
/insert
and I have not implemented a reverse_iterator
, although I have made my own iterator
and const_iterator
class.
# include <memory>
// I included the below two headers for my tests
# include <iostream>
# include <string>
namespace mynamespace
{
template <typename T, typename A>
class vector;
template<typename Vec_T>
class vector_const_iterator;
template<typename Vec_T>
class vector_iterator
{
public:
typedef vector_iterator<Vec_T> this_t;
typedef typename Vec_T::value_type T;
friend Vec_T;
vector_iterator(T *ptr_, Vec_T const &cont)
: container(cont),
ptr(ptr_)
{
}
vector_iterator(this_t const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
vector_iterator(vector_const_iterator<Vec_T> const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
~vector_iterator()
{
}
bool operator!=(this_t const &rhs) const
{
return (ptr != rhs.ptr);
}
bool operator!=(T *ptr_) const
{
return (ptr != ptr_);
}
bool operator==(this_t const &rhs) const
{
return (ptr == rhs.ptr);
}
bool operator==(T *ptr_) const
{
return (ptr == ptr_);
}
bool operator>(this_t const &rhs) const
{
return (ptr > rhs.ptr);
}
bool operator>(T *ptr_) const
{
return (ptr > ptr_);
}
bool operator>=(this_t const &rhs) const
{
return (ptr >= rhs.ptr);
}
bool operator>=(T *ptr_) const
{
return (ptr >= ptr_);
}
bool operator<(this_t const &rhs) const
{
return (ptr < rhs.ptr);
}
bool operator<(T *ptr_) const
{
return (ptr < ptr_);
}
bool operator<=(this_t const &rhs) const
{
return (ptr <= rhs.ptr);
}
bool operator<=(T *ptr_) const
{
return (ptr <= ptr_);
}
T &operator*()
{
if (is_valid_ptr())
{
return (*ptr);
}
throw std::exception("vector_iterator: pointer out of safe range when dereferencing (operator*)");
}
T *operator->()
{
if (is_valid_ptr())
{
return (ptr);
}
throw std::exception("vector_iterator: pointer out of safe range when dereferencing (operator->)");
}
this_t operator++(int)
{
this_t holder(*this);
++(*this);
return (holder);
}
this_t &operator++()
{
++ptr;
return (*this);
}
this_t operator+=(unsigned offset)
{
ptr += offset;
return (*this);
}
this_t &operator-=(unsigned offset)
{
ptr -= offset;
return (*this);
}
T &operator[](unsigned offset) const
{
this_t holder(*this);
holder.ptr += offset;
if (holder.is_valid_ptr())
{
return (*(ptr + offset));
}
throw std::exception("vector_iterator: pointer out of safe range when dereferencing (operator[])");
}
this_t &operator=(this_t const &rhs)
{
ptr = rhs.ptr;
return (*this);
}
this_t operator+(unsigned offset)
{
return (this_t(ptr + offset, container));
}
this_t operator-(unsigned offset)
{
return (this_t(ptr - offset, container));
}
unsigned operator-(this_t const &rhs)
{
return (ptr - rhs.ptr);
}
this_t operator--(int)
{
this_t holder(*this);
--(*this);
return (holder);
}
this_t &operator--()
{
--ptr;
return (*this);
}
operator vector_const_iterator<Vec_T>()
{
return (vector_const_iterator<Vec_T>(ptr, container));
}
private:
Vec_T const &container;
T *ptr;
bool is_valid_ptr() const
{
return (ptr >= container.mem_start
&& ptr < container.seq_end);
}
bool is_valid_ptr2() const
{
return (ptr >= container.mem_start
&& ptr <= container.seq_end);
}
};
template<typename Vec_T>
class vector_const_iterator
{
public:
typedef vector_const_iterator<Vec_T> this_t;
typedef typename Vec_T::value_type T;
friend Vec_T;
vector_const_iterator(T *ptr_, Vec_T const &cont)
: container(cont),
ptr(ptr_)
{
}
vector_const_iterator(this_t const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
vector_const_iterator(vector_iterator<Vec_T> const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
~vector_const_iterator()
{
}
bool operator!=(this_t const &rhs) const
{
return (ptr != rhs.ptr);
}
bool operator!=(T *ptr_) const
{
return (ptr != ptr_);
}
bool operator==(this_t const &rhs) const
{
return (ptr == rhs.ptr);
}
bool operator==(T *ptr_) const
{
return (ptr == ptr_);
}
bool operator>(this_t const &rhs) const
{
return (ptr > rhs.ptr);
}
bool operator>(T *ptr_) const
{
return (ptr > ptr_);
}
bool operator>=(this_t const &rhs) const
{
return (ptr >= rhs.ptr);
}
bool operator>=(T *ptr_) const
{
return (ptr >= ptr_);
}
bool operator<(this_t const &rhs) const
{
return (ptr < rhs.ptr);
}
bool operator<(T *ptr_) const
{
return (ptr < ptr_);
}
bool operator<=(this_t const &rhs) const
{
return (ptr <= rhs.ptr);
}
bool operator<=(T *ptr_) const
{
return (ptr <= ptr_);
}
T const &operator*()
{
if (is_valid_ptr())
{
return (*ptr);
}
throw std::exception("vector_const_iterator: pointer out of safe range when dereferencing (operator*)");
}
T const *operator->()
{
if (is_valid_ptr())
{
return (ptr);
}
throw std::exception("vector_const_iterator: pointer out of safe range when dereferencing (operator->)");
}
this_t operator++(int)
{
this_t holder(*this);
++(*this);
return (holder);
}
this_t &operator++()
{
++ptr;
return (*this);
}
this_t operator+=(unsigned offset)
{
ptr += offset;
return (*this);
}
this_t &operator-=(unsigned offset)
{
ptr -= offset;
return (*this);
}
T &operator[](unsigned offset) const
{
this_t holder(*this);
holder.ptr += offset;
if (holder.is_valid_ptr())
{
return (*(ptr + offset));
}
throw std::exception("vector_const_iterator: pointer out of safe range when dereferencing (operator[])");
}
this_t &operator=(this_t const &rhs)
{
ptr = rhs.ptr;
return (*this);
}
this_t operator+(unsigned offset)
{
return (this_t(ptr + offset, container));
}
this_t operator-(unsigned offset)
{
return (this_t(ptr - offset, container));
}
unsigned operator-(this_t const &rhs)
{
return (ptr - rhs.ptr);
}
this_t operator--(int)
{
this_t holder(*this);
--(*this);
return (holder);
}
this_t &operator--()
{
--ptr;
return (*this);
}
operator vector_iterator<Vec_T>()
{
return (vector_iterator<Vec_T>(ptr, container));
}
private:
Vec_T const &container;
T *ptr;
bool is_valid_ptr() const
{
return (ptr >= container.mem_start
&& ptr < container.seq_end);
}
bool is_valid_ptr2() const
{
return (ptr >= container.mem_start
&& ptr <= container.seq_end);
}
};
template<typename Vec_T>
class vector_const_reverse_iterator;
template<typename Vec_T>
class vector_reverse_iterator
{
public:
typedef vector_reverse_iterator<Vec_T> this_t;
typedef typename Vec_T::value_type T;
friend Vec_T;
vector_reverse_iterator(T *ptr_, Vec_T const &cont)
: container(cont),
ptr(ptr_)
{
}
vector_reverse_iterator(this_t const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
vector_reverse_iterator(vector_const_reverse_iterator<Vec_T> const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
~vector_reverse_iterator()
{
}
bool operator!=(this_t const &rhs) const
{
return (ptr != rhs.ptr);
}
bool operator!=(T *ptr_) const
{
return (ptr != ptr_);
}
bool operator==(this_t const &rhs) const
{
return (ptr == rhs.ptr);
}
bool operator==(T *ptr_) const
{
return (ptr == ptr_);
}
bool operator>(this_t const &rhs) const
{
return (ptr < rhs.ptr);
}
bool operator>(T *ptr_) const
{
return (ptr < ptr_);
}
bool operator>=(this_t const &rhs) const
{
return (ptr <= rhs.ptr);
}
bool operator>=(T *ptr_) const
{
return (ptr <= ptr_);
}
bool operator<(this_t const &rhs) const
{
return (ptr > rhs.ptr);
}
bool operator<(T *ptr_) const
{
return (ptr > ptr_);
}
bool operator<=(this_t const &rhs) const
{
return (ptr >= rhs.ptr);
}
bool operator<=(T *ptr_) const
{
return (ptr >= ptr_);
}
T &operator*()
{
if (is_valid_ptr())
{
return (*ptr);
}
throw std::exception("vector_reverse_iterator: pointer out of safe range when dereferencing (operator*)");
}
T *operator->()
{
if (is_valid_ptr())
{
return (ptr);
}
throw std::exception("vector_reverse_iterator: pointer out of safe range when dereferencing (operator->)");
}
this_t operator++(int)
{
this_t holder(*this);
--(*this);
return (holder);
}
this_t &operator++()
{
--ptr;
return (*this);
}
this_t operator+=(unsigned offset)
{
ptr -= offset;
return (*this);
}
this_t &operator-=(unsigned offset)
{
ptr += offset;
return (*this);
}
T &operator[](unsigned offset) const
{
this_t holder(*this);
holder.ptr -= offset;
if (holder.is_valid_ptr())
{
return (*(ptr - offset));
}
throw std::exception("vector_reverse_iterator: pointer out of safe range when dereferencing (operator[])");
}
this_t &operator=(this_t const &rhs)
{
ptr = rhs.ptr;
return (*this);
}
this_t operator+(unsigned offset)
{
return (this_t(ptr + offset, container));
}
this_t operator-(unsigned offset)
{
return (this_t(ptr - offset, container));
}
unsigned operator-(this_t const &rhs)
{
return (rhs.ptr - ptr);
}
this_t operator--(int)
{
this_t holder(*this);
++(*this);
return (holder);
}
this_t &operator--()
{
--ptr;
return (*this);
}
operator vector_const_reverse_iterator<Vec_T>()
{
return (vector_const_reverse_iterator<Vec_T>(ptr, container));
}
private:
Vec_T const &container;
T *ptr;
bool is_valid_ptr() const
{
return (ptr >= container.mem_start
&& ptr < container.seq_end);
}
bool is_valid_ptr2() const
{
return (ptr >= (container.mem_start - 1)
&& ptr < container.seq_end);
}
};
template<typename Vec_T>
class vector_const_reverse_iterator
{
public:
typedef vector_const_reverse_iterator<Vec_T> this_t;
typedef typename Vec_T::value_type T;
friend Vec_T;
vector_const_reverse_iterator(T *ptr_, Vec_T const &cont)
: container(cont),
ptr(ptr_)
{
}
vector_const_reverse_iterator(this_t const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
vector_const_reverse_iterator(vector_reverse_iterator<Vec_T> const &rhs)
: container(rhs.container),
ptr(rhs.ptr)
{
}
~vector_const_reverse_iterator()
{
}
bool operator!=(this_t const &rhs) const
{
return (ptr != rhs.ptr);
}
bool operator!=(T *ptr_) const
{
return (ptr != ptr_);
}
bool operator==(this_t const &rhs) const
{
return (ptr == rhs.ptr);
}
bool operator==(T *ptr_) const
{
return (ptr == ptr_);
}
bool operator>(this_t const &rhs) const
{
return (ptr < rhs.ptr);
}
bool operator>(T *ptr_) const
{
return (ptr < ptr_);
}
bool operator>=(this_t const &rhs) const
{
return (ptr <= rhs.ptr);
}
bool operator>=(T *ptr_) const
{
return (ptr <= ptr_);
}
bool operator<(this_t const &rhs) const
{
return (ptr > rhs.ptr);
}
bool operator<(T *ptr_) const
{
return (ptr > ptr_);
}
bool operator<=(this_t const &rhs) const
{
return (ptr >= rhs.ptr);
}
bool operator<=(T *ptr_) const
{
return (ptr >= ptr_);
}
T const &operator*()
{
if (is_valid_ptr())
{
return (*ptr);
}
throw std::exception("vector_const_reverse_iterator: pointer out of safe range when dereferencing (operator*)");
}
T const *operator->()
{
if (is_valid_ptr())
{
return (ptr);
}
throw std::exception("vector_const_reverse_iterator: pointer out of safe range when dereferencing (operator->)");
}
this_t operator++(int)
{
this_t holder(*this);
--(*this);
return (holder);
}
this_t &operator++()
{
--ptr;
return (*this);
}
this_t operator+=(unsigned offset)
{
ptr -= offset;
return (*this);
}
this_t &operator-=(unsigned offset)
{
ptr += offset;
return (*this);
}
T &operator[](unsigned offset) const
{
this_t holder(*this);
holder.ptr -= offset;
if (holder.is_valid_ptr())
{
return (*(ptr - offset));
}
throw std::exception("vector_const_reverse_iterator: pointer out of safe range when dereferencing (operator[])");
}
this_t &operator=(this_t const &rhs)
{
ptr = rhs.ptr;
return (*this);
}
this_t operator+(unsigned offset)
{
return (this_t(ptr - offset, container));
}
this_t operator-(unsigned offset)
{
return (this_t(ptr + offset, container));
}
unsigned operator-(this_t const &rhs)
{
return (rhs.ptr - ptr);
}
this_t operator--(int)
{
this_t holder(*this);
++(*this);
return (holder);
}
this_t &operator--()
{
++ptr;
return (*this);
}
operator vector_reverse_iterator<Vec_T>()
{
return (vector_reverse_iterator<Vec_T>(ptr, container));
}
private:
Vec_T const &container;
T *ptr;
bool is_valid_ptr() const
{
return (ptr >= container.mem_start
&& ptr < container.seq_end);
}
bool is_valid_ptr2() const
{
return (ptr >= (container.mem_start - 1)
&& ptr < container.seq_end);
}
};
template <typename T, typename A = std::allocator<T> >
class vector
{
public:
typedef T value_type;
typedef value_type &reference;
typedef value_type const &const_reference;
typedef value_type *pointer;
typedef value_type const *const_pointer;
typedef vector<T, A> this_t;
typedef A allocator_t;
typedef vector_iterator<this_t> iterator;
typedef vector_const_iterator<this_t> const_iterator;
typedef vector_reverse_iterator<this_t> reverse_iterator;
typedef vector_const_reverse_iterator<this_t> const_reverse_iterator;
friend class iterator;
friend class const_iterator;
friend class reverse_iterator;
friend class const_reverse_iterator;
vector()
: mem_start(pointer()),
seq_end(pointer()),
mem_end(pointer())
{
}
vector(this_t const &rhs)
: mem_start(pointer()),
seq_end(pointer()),
mem_end(pointer())
{
assign(rhs);
}
vector(unsigned count, value_type const &val = value_type())
: mem_start(pointer()),
seq_end(pointer()),
mem_end(pointer())
{
assign(count, val);
}
template<unsigned N>
vector(value_type (&arr)[N])
: mem_start(pointer()),
seq_end(pointer()),
mem_end(pointer())
{
assign(&arr[0], &arr[N]);
}
vector(pointer start, pointer end)
: mem_start(pointer()),
seq_end(pointer()),
mem_end(pointer())
{
assign(start, end);
}
~vector()
{
wipe_all();
}
template<unsigned N>
void assign(value_type (&arr)[N])
{
assign(&arr[0], &arr[N]);
}
void assign(unsigned count, value_type const &val = value_type())
{
value_type v = val;
if (points_to(&val))
{
v = val;
}
if (is_clean() || size() == 0)
{
allocate(count);
}
else if (count < capacity())
{
wipe_values();
}
else
{
reallocate(count);
wipe_values();
}
seq_end = std::uninitialized_fill_n(mem_start, count, v);
}
void assign(pointer first, pointer last)
{
if (!points_to(first) && !points_to(last))
{
if (is_clean() || size() == 0)
{
allocate(last - first);
}
else if ((last - first) < capacity())
{
wipe_values();
}
else
{
reallocate((last - first));
wipe_values();
}
seq_end = std::uninitialized_copy(first, last, mem_start);
}
}
void assign(this_t const &rhs)
{
if (&rhs != this)
{
if (rhs.size() <= capacity())
{
wipe_values();
}
else if (is_clean() || size() == 0)
{
allocate(rhs.size());
}
else
{
reallocate(rhs.size());
wipe_values();
}
seq_end = std::uninitialized_copy(rhs.mem_start, rhs.seq_end, mem_start);
}
}
this_t &operator=(this_t const &rhs)
{
assign(rhs);
return (*this);
}
void swap(this_t &rhs)
{
std::swap(mem_start, rhs.mem_start);
std::swap(seq_end, rhs.seq_end);
std::swap(mem_end, rhs.mem_end);
}
void push_front(const_reference value)
{
T to_push = value;
if (points_to(&value))
{
to_push = value;
}
this_t holder;
holder.allocate(capacity() + 1);
holder.push_back(to_push);
holder.seq_end = std::uninitialized_copy(mem_start, seq_end, holder.mem_start + 1);
swap(holder);
}
void push_back(const_reference value)
{
T to_push = value;
if (is_clean() || size() == 0)
{
allocate(2);
}
else if ((size() + 1) > capacity())
{
reallocate(capacity() * 1.5);
}
else if (points_to(&value))
{
to_push = value;
}
allocator_t al;
al.construct(seq_end++, to_push);
}
void pop_front()
{
if ((mem_start + 1) == seq_end)
{
this_t holder;
holder.allocate(capacity());
swap(holder);
}
else
{
this_t holder(mem_start + 1, seq_end);
holder.reallocate(capacity());
swap(holder);
}
}
void pop_back()
{
allocator_t al;
al.destroy(seq_end - 1);
--seq_end;
}
void insert(iterator position, const value_type &val)
{
if (position.is_valid_ptr2())
{
if (position == seq_end)
{
push_back(val);
}
else if (position = mem_start)
{
push_front(val);
}
else
{
this_t holder;
holder.allocate(capacity() + 1);
holder.seq_end = std::uninitialized_copy(mem_start, position, holder.mem_start);
holder.push_back(val);
holder.seq_end = std::uninitialized_copy(position, seq_end, holder.seq_end);
swap(holder);
}
}
}
void insert(iterator position, unsigned count, const value_type &val)
{
if (position.is_valid_ptr2())
{
for (int i = 0; i < size(); ++i)
{
if (position == seq_end)
{
while (count--)
{
push_back(val);
}
}
else if(position == mem_start)
{
while (count--)
{
push_front(val);
}
}
else
{
this_t holder;
holder.allocate(capacity() + count);
holder.seq_end = std::uninitialized_copy(mem_start, position, holder.mem_start);
holder.seq_end = std::uninitialized_fill_n(holder.seq_end, count, val);
holder.seq_end = std::uninitialized_copy(position, seq_end, holder.seq_end);
swap(holder);
}
}
}
}
void insert(iterator position, iterator first, iterator last)
{
if (first.is_valid_ptr() && last.is_valid_ptr2())
{
if (first == last)
{
insert(position, *first);
}
else if (first == begin())
{
while (last != first)
{
push_front(last--);
}
}
else if (last == end())
{
while (first != last)
{
push_back(first++);
}
}
else
{
this_t holder;
allocator_t al;
if (holder.allocate(capacity() + (last - first)))
{
holder.seq_end = std::uninitialized_copy(mem_start, first.ptr, holder.mem_start);
while (first != last)
{
push_back(first++);
}
holder.seq_end = std::uninitialized_copy((last.ptr + 1), seq_end, holder.seq_end);
swap(holder);
}
}
}
}
void erase(iterator it)
{
if (it.is_valid_ptr())
{
if (it == mem_start)
{
pop_front();
}
else if (it == (seq_end - 1))
{
pop_back();
}
else
{
erase(it, it + 1);
}
}
}
void erase(iterator start, iterator end_)
{
if (start.is_valid_ptr() && end_.is_valid_ptr2())
{
if (start == end_)
{
erase(start);
}
else if (start == begin())
{
while (end_-- != start)
{
pop_front();
}
}
else if (end_ == end())
{
while (end_-- != start)
{
pop_back();
}
}
else
{
this_t holder;
allocator_t al;
if (holder.allocate(capacity()))
{
holder.seq_end = std::uninitialized_copy(mem_start, start.ptr, holder.mem_start);
holder.seq_end = std::uninitialized_copy(end_.ptr, seq_end, holder.seq_end);
swap(holder);
}
}
}
}
void clear()
{
wipe_values();
seq_end = mem_start;
}
void reserve(unsigned count)
{
if (count < capacity())
{
reallocate(count);
}
}
reference operator[](unsigned offset)
{
return (*(mem_start + offset));
}
const_reference operator[](unsigned offset) const
{
return (*(mem_start + offset));
}
reference at(unsigned offset)
{
return (*(begin() + offset));
}
const_reference at(unsigned offset) const
{
return (*(begin() + offset));
}
reference front()
{
return (*(begin()));
}
const_reference front() const
{
return (*(cbegin()));
}
reference back()
{
return (*(end() - 1));
}
const_reference back() const
{
return (*(cend() - 1));
}
iterator begin()
{
return (iterator(mem_start, (*this)));
}
iterator end()
{
return (iterator(seq_end, (*this)));
}
const_iterator cbegin()
{
return (const_iterator(mem_start, (*this)));
}
const_iterator cend()
{
return (const_iterator(seq_end, (*this)));
}
reverse_iterator rbegin()
{
return (reverse_iterator(seq_end - 1, (*this)));
}
reverse_iterator rend()
{
return (reverse_iterator(mem_start - 1, (*this)));
}
const_reverse_iterator crbegin() const
{
return (const_reverse_iterator(seq_end - 1, (*this)));
}
const_reverse_iterator crend() const
{
return (const_reverse_iterator(mem_start - 1, (*this)));
}
iterator make_iterator(unsigned offset)
{
return (iterator(mem_start + offset, (*this)));
}
iterator make_iterator(reference position)
{
return (iterator(&position, (*this)));
}
const_iterator make_const_iterator(unsigned offset)
{
return (const_iterator(mem_start + offset, (*this)));
}
const_iterator make_const_iterator(reference position)
{
return (const_iterator(&position, (*this)));
}
void shrink_to_fit()
{
if (size() < capacity())
{
this_t holder = *this;
swap(holder);
}
}
unsigned size() const
{
return (seq_end - mem_start);
}
unsigned capacity() const
{
return (mem_end - mem_start);
}
bool empty() const
{
return (is_clean() || size() == 0);
}
private:
pointer mem_start, seq_end, mem_end;
bool points_to(const_pointer ptr)
{
return (ptr >= mem_start && ptr <= seq_end);
}
bool allocate(unsigned size)
{
mem_start = pointer();
seq_end = pointer();
mem_end = pointer();
allocator_t al;
if (size < al.max_size())
{
mem_start = al.allocate(size);
seq_end = mem_start;
mem_end = mem_start + size;
return (true);
}
return (false);
}
void reallocate(unsigned size)
{
allocator_t al;
while (size == capacity())
{
size *= 2;
}
pointer new_memstart = al.allocate(size), new_seqend;
new_seqend = std::uninitialized_copy(mem_start, seq_end, new_memstart);
wipe_all();
if (allocate(size))
{
seq_end = std::uninitialized_copy(new_memstart, new_seqend, mem_start);
}
}
void wipe_values()
{
allocator_t al;
if (!is_clean())
{
unsigned sz = size(), sz2 = size();
while (sz-- != 0)
{
al.destroy(mem_start++);
}
mem_start -= sz2;
}
}
void wipe_all()
{
if (!is_clean())
{
allocator_t al;
wipe_values();
unsigned cp = capacity();
al.deallocate(mem_start, cp);
}
}
bool is_clean() const
{
return (mem_start == pointer() &&
seq_end == pointer() && mem_end == pointer());
}
};
}
reverse_iterator
is specified to bereverse_iterator<iterator>
. You are not allowed to roll out your ownvector_reverse_iterator
. \$\endgroup\$