I am practicing move semantics and placement new by writing a custom Vector class but I am not confident that I use them right. I would really appreciate some pieces of advice regarding my code.
Here is my Vector header
#ifndef VECTOR_VECTOR_H
#define VECTOR_VECTOR_H
#include <iostream>
#include <utility>
#include <iterator>
#include <algorithm>
#define _NOEXCEPT noexcept
template < typename T > class Vector { public: explicit Vector( size_t = INITIAL_CAPACITY );
Vector( size_t, const T & );
template < typename InputIterator > Vector( InputIterator, InputIterator );
Vector( const Vector & );
Vector( Vector && ) _NOEXCEPT;
Vector &operator=( const Vector & );
Vector &operator=( Vector && ) _NOEXCEPT;
Vector &operator=( std::initializer_list < T > );
~Vector();
template < class InputIterator > void assign( InputIterator first, InputIterator last );
void assign( size_t n, const T &val );
void assign( std::initializer_list < T > il );
void push_back( const T & );
void push_back( T && );
void pop_back() _NOEXCEPT;
void reserve( size_t );
void resize( size_t );
void resize( size_t, const T & );
T &operator[]( size_t );
const T &operator[]( size_t ) const;
T &at( size_t );
const T &at( size_t ) const;
T &front();
const T &front() const;
T &back();
const T &back() const;
T *data() _NOEXCEPT;
const T *data() const _NOEXCEPT;
bool empty() const _NOEXCEPT;
size_t size() const _NOEXCEPT;
size_t capacity() const _NOEXCEPT;
bool contains( const T & ) const _NOEXCEPT;
void shrink_to_fit();
void swap( Vector & );
void clear() _NOEXCEPT;
#include "const_iterator.h"
#include "iterator.h"
#include "const_reverse_iterator.h"
#include "reverse_iterator.h"
iterator begin() _NOEXCEPT { return iterator( m_data ); }
iterator end() _NOEXCEPT { return iterator( m_data + m_size ); }
reverse_iterator rbegin() _NOEXCEPT { return reverse_iterator( m_data + m_size - 1 ); }
reverse_iterator rend() _NOEXCEPT { return reverse_iterator( m_data
- 1 ); }
const_iterator begin() const _NOEXCEPT { return const_iterator( m_data ); }
const_iterator end() const _NOEXCEPT { return const_iterator( m_data + m_size ); }
const_reverse_iterator rbegin() const _NOEXCEPT { return const_reverse_iterator( m_data + m_size - 1 ); }
const_reverse_iterator rend() const _NOEXCEPT { return const_reverse_iterator( m_data - 1 ); }
const_iterator cbegin() const _NOEXCEPT { return const_iterator( m_data ); }
const_iterator cend() const _NOEXCEPT { return const_iterator( m_data + m_size ); }
const_reverse_iterator crbegin() const _NOEXCEPT { return const_reverse_iterator( m_data + m_size - 1 ); }
const_reverse_iterator crend() const _NOEXCEPT { return const_reverse_iterator( m_data - 1 ); }
iterator erase( const_iterator );
iterator erase( const_iterator, const_iterator );
iterator insert( const_iterator position, const T &val );
iterator insert( const_iterator position, size_t n, const T &val );
template < class InputIterator > iterator insert( const_iterator position, InputIterator first, InputIterator last );
iterator insert( const_iterator position, T &&val );
iterator insert( const_iterator position, std::initializer_list < T > il );
private: void allocateMemory_( T *&, size_t );
void moveFrom_( Vector && ) _NOEXCEPT;
void destructObjects_() _NOEXCEPT;
bool makeSpace( iterator, size_t );
private: T *m_data; size_t m_size; size_t m_capacity;
static const int INITIAL_CAPACITY = 2; static const int FACTOR
= 2; };
template < typename T > bool operator==( const Vector < T > &lhs, const Vector < T > &rhs );
template < typename T > bool operator!=( const Vector < T > &lhs, const Vector < T > &rhs );
template < typename T > bool operator>( const Vector < T > &lhs, const Vector < T > &rhs );
template < typename T > bool operator>=( const Vector < T > &lhs, const Vector < T > &rhs );
template < typename T > bool operator<( const Vector < T > &lhs, const Vector < T > &rhs );
template < typename T > bool operator<=( const Vector < T > &lhs, const Vector < T > &rhs );
#include "vector_implementations.h"
#endif //VECTOR_VECTOR_H
My Vector implementations are in a separate header for readability purposes.
#ifndef VECTOR_VECTOR_IMPLEMENTATIONS_H
#define VECTOR_VECTOR_IMPLEMENTATIONS_H
template < typename T >
inline Vector < T >::Vector( size_t capacity ) :m_data( nullptr ), m_size( 0 ), m_capacity( capacity ) {
allocateMemory_( m_data, m_capacity );
}
template < typename T >
inline Vector < T >::Vector( const Vector &rhs ) : Vector( rhs . m_size ) {
std::copy( rhs . begin(), rhs . end(), begin());
}
template < typename T >
inline Vector < T >::Vector( Vector &&rhs ) _NOEXCEPT {
moveFrom_( std::move( rhs ));
}
template < typename T >
inline Vector < T > &Vector < T >::operator=( const Vector &rhs ) {
if ( this != &rhs ) {
//copy-swap idiom
Vector tmp( rhs );
swap( tmp );
}
return *this;
}
template < typename T >
inline Vector < T > &Vector < T >::operator=( Vector &&rhs ) _NOEXCEPT {
if ( this != &rhs ) {
clear();
moveFrom_( std::move( rhs ));
}
return *this;
}
template < typename T >
inline Vector < T >::~Vector() {
clear();
}
template < typename T >
inline void Vector < T >::pop_back() _NOEXCEPT {
m_data[ --m_size ] . ~T();
}
template < typename T >
inline void Vector < T >::push_back( const T &element ) {
if ( !m_data || m_size == m_capacity ) {
reserve( m_capacity ? m_capacity * FACTOR : INITIAL_CAPACITY );
}
new( m_data + m_size )T( element );
++m_size;
}
template < typename T >
inline void Vector < T >::push_back( T &&element ) {
if ( !m_data || m_size == m_capacity ) {
reserve( m_capacity ? m_capacity * FACTOR : INITIAL_CAPACITY );
}
new( m_data + m_size )T( std::move( element ));
++m_size;
}
template < typename T >
inline T &Vector < T >::operator[]( size_t idx ) {
return *( m_data + idx );
}
template < typename T >
inline const T &Vector < T >::operator[]( size_t idx ) const {
return *( m_data + idx );
}
template < typename T >
inline T &Vector < T >::at( size_t idx ) {
if ( idx >= m_size ) {
throw ( std::out_of_range( "Invalid index" ));
}
return *( m_data + idx );
}
template < typename T >
inline const T &Vector < T >::at( size_t idx ) const {
if ( idx >= m_size ) {
throw ( std::out_of_range( "Invalid index" ));
}
return *( m_data + idx );
}
template < typename T >
inline T &Vector < T >::front() {
return *m_data;
}
template < typename T >
inline const T &Vector < T >::front() const {
return *m_data;
}
template < typename T >
inline T &Vector < T >::back() {
return *( m_data + m_size - 1 );
}
template < typename T >
inline const T &Vector < T >::back() const {
return *( m_data + m_size - 1 );
}
template < typename T >
inline T *Vector < T >::data() _NOEXCEPT {
return m_data;
}
template < typename T >
inline const T *Vector < T >::data() const _NOEXCEPT {
return m_data;
}
template < typename T >
inline bool Vector < T >::empty() const _NOEXCEPT {
return ( m_size == 0 );
}
template < typename T >
inline size_t Vector < T >::size() const _NOEXCEPT {
return m_size;
}
template < typename T >
inline size_t Vector < T >::capacity() const _NOEXCEPT {
return m_capacity;
}
template < typename T >
inline bool Vector < T >::contains( const T &element ) const _NOEXCEPT {
for ( int i = 0 ;
i < m_size ;
++i ) {
if ( m_data[ i ] == element ) {
return true;
}
}
return false;
}
template < typename T >
inline void Vector < T >::shrink_to_fit() {
Vector( *this ) . swap( *this );
}
template < typename T >
inline void Vector < T >::swap( Vector &rhs ) {
//check ADL look up
using std::swap;
swap( m_data, rhs . m_data );
swap( m_size, rhs . m_size );
swap( m_capacity, rhs . m_capacity );
}
template < typename T >
inline void Vector < T >::clear() _NOEXCEPT {
destructObjects_();
operator delete( m_data );
m_capacity = 0;
}
template < typename T >
inline void Vector < T >::allocateMemory_( T *&destination, size_t capacity ) {
destination = static_cast< T * >( operator new[]( capacity * sizeof( T )));
}
template < typename T >
inline void Vector < T >::moveFrom_( Vector &&rhs ) _NOEXCEPT {
m_data = rhs . m_data;
rhs . m_data = nullptr;
m_size = rhs . m_size;
m_capacity = rhs . m_capacity;
}
template < typename T >
inline void Vector < T >::destructObjects_() _NOEXCEPT {
while ( !empty()) {
pop_back();
}
}
template < typename T >
inline void Vector < T >::reserve( size_t size ) {
if ( size <= m_capacity ) {
return;
}
T *newData = nullptr;
allocateMemory_( newData, size );
size_t i = 0;
for ( ; i < m_size ;
++i ) {
new( newData + i )T( std::move( m_data[ i ] ));
}
clear();
m_size = i;
m_data = newData;
m_capacity = size;
}
template < typename T >
inline void Vector < T >::resize( size_t size ) {
resize( size, T());
}
template < typename T >
inline void Vector < T >::resize( size_t size, const T &value ) {
reserve( size );
if ( size <= m_size ) {
while ( m_size > size ) {
pop_back();
}
} else {
while ( m_size < size ) {
push_back( value );
}
}
}
template < typename T >
inline typename Vector < T >::iterator Vector < T >::erase( typename Vector < T >::const_iterator position ) {
//std::advance(it,std::distance(cbegin(),position));
//iterator iter = begin() + ( position - cbegin() );
//std::move( iter + 1, end(), iter );
//pop_back();
//return iter;
return erase( position, position + 1 );
}
template < typename T >
inline typename Vector < T >::iterator Vector < T >::erase
( typename Vector < T >::const_iterator first, typename Vector < T >::const_iterator last ) {
//UB on invalid range
iterator iter = begin() + ( first - cbegin());
int removed_elements = last - first;
std::move( last, cend(), iter );
while ( removed_elements-- ) {
pop_back();
}
return iter;
}
template < typename T >
bool Vector < T >::makeSpace( Vector::iterator position, size_t space ) {
size_t elementsToMove = end() - position;
std::cout << "POSITION " << elementsToMove << std::endl;
if ( m_size + space >= m_capacity ) {
reserve( m_capacity ? m_capacity * FACTOR : INITIAL_CAPACITY );
}
for ( int i = 0 ;
i < elementsToMove ;
++i ) {
new( m_data + m_size + space - i ) T( std::move( m_data[ m_size - i ] ));
}
m_size += space;
}
template < typename T >
Vector < T >::Vector( size_t n, const T &val ) :Vector( n ) {
while ( n-- ) {
push_back( val );
}
}
template < typename T >
template < typename InputIterator >
Vector < T >::Vector( InputIterator first, InputIterator last ) {
size_t numElements = last - first;
allocateMemory_( m_data, numElements );
while ( first != last ) {
push_back( *first );
++first;
}
}
template < typename T >
Vector < T > &Vector < T >::operator=( std::initializer_list < T > il ) {
Vector tmp( il . begin(), il . end());
swap( tmp );
return *this;
}
template < typename T >
Vector::iterator Vector < T >::insert( Vector::const_iterator position, const T &val ) {
size_t offset = position - cbegin();
makeSpace( begin() + offset - 1, 1 );
m_data[ offset ] = val;
return ( begin() + offset );
}
template < typename T >
Vector::iterator Vector < T >::insert( Vector::const_iterator position, size_t n, const T &val ) {
size_t offset = position - cbegin();
makeSpace( begin() + offset - 1, n );
for ( int i = 0 ; i < n ; ++i ) {
m_data[ offset + i ] = val;
}
return ( begin() + offset );
}
template < typename T >
template < class InputIterator >
Vector::iterator Vector < T >::insert( Vector::const_iterator position, InputIterator first, InputIterator last ) {
size_t offset = position - cbegin();
makeSpace( begin() + offset - 1, last - first );
int i = 0;
while ( first != last ) {
m_data[ offset + i ] = *first;
++first;
++i;
}
return ( begin() + offset );
}
template < typename T >
Vector::iterator Vector < T >::insert( Vector::const_iterator position, T &&val ) {
size_t offset = position - cbegin();
makeSpace( begin() + offset - 1, 1 );
m_data[ offset ] = std::move( val );
return ( begin() + offset );
}
template < typename T >
Vector::iterator Vector < T >::insert( Vector::const_iterator position, std::initializer_list < T > il ) {
return insert( position, il . begin(), il . end());
}
template < typename T >
template < class InputIterator >
void Vector < T >::assign( InputIterator first, InputIterator last ) {
swap( Vector( first, last ));
}
template < typename T >
void Vector < T >::assign( size_t n, const T &val ) {
swap( Vector( n, val ));
}
template < typename T >
void Vector < T >::assign( std::initializer_list < T > il ) {
swap( Vector( il ));
}
template < typename T >
inline bool operator==( const Vector < T > &lhs, const Vector < T > &rhs ) {
if ( lhs . size() != rhs . size()) {
return false;
}
for ( int i = 0 ;
i < lhs . size() ;
++i ) {
if ( lhs[ i ] != rhs[ i ] ) {
return false;
}
}
return true;
}
template < typename T >
inline bool operator!=( const Vector < T > &lhs, const Vector < T > &rhs ) {
return !( lhs == rhs );
}
template < typename T >
inline bool operator>( const Vector < T > &lhs, const Vector < T > &rhs ) {
return rhs < lhs;
}
template < typename T >
inline bool operator>=( const Vector < T > &lhs, const Vector < T > &rhs ) {
return !( lhs < rhs );
}
template < typename T >
inline bool operator<( const Vector < T > &lhs, const Vector < T > &rhs ) {
int i = 0;
while ( i < lhs . size() && i < rhs . size() && lhs[ i ] == rhs[ i ] ) {
++i;
}
if ( i == lhs . size() || i == rhs . size()) {
return lhs . size() < rhs . size();
}
return lhs[ i ] < rhs[ i ];
}
template < typename T >
inline bool operator<=( const Vector < T > &lhs, const Vector < T > &rhs ) {
return !( rhs < lhs );
}
#endif //VECTOR_VECTOR_IMPLEMENTATIONS_H
And I will just post the code for one of the iterators type, because the rest are identical.
#ifndef VECTOR_CONST_REVERSE_ITERATOR_H
#define VECTOR_CONST_REVERSE_ITERATOR_H
class const_reverse_iterator : public std::iterator<std::random_access_iterator_tag, const T>
{
friend class Vector;
friend class const_iterator;
public:
const_reverse_iterator( const T* data = nullptr ) : m_data( data )
{}
const T& operator*() const
{
return *m_data;
}
const T* operator->() const
{
return m_data;
}
const_reverse_iterator& operator++()
{
--m_data;
return *this;
}
const_reverse_iterator operator++( int )
{
const_reverse_iterator res( *this );
--( *this );
return res;
}
const_reverse_iterator& operator+=( int n )
{
while ( --n )
++( *this );
return *this;
}
const_reverse_iterator operator+( int n ) const
{
const_reverse_iterator tmp( *this );
tmp -= n;
return tmp;
}
const_reverse_iterator& operator--()
{
++m_data;
return *this;
}
const_reverse_iterator operator--( int )
{
const_reverse_iterator res( *this );
++( *this );
return res;
}
const_reverse_iterator& operator-=( int n )
{
while ( n-- )
--( *this );
return *this;
}
const_reverse_iterator operator-( int n ) const
{
const_reverse_iterator tmp( *this );
tmp += n;
return tmp;
}
ptrdiff_t operator- ( const const_reverse_iterator& rhs ) const
{
return m_data - rhs.m_data;
}
const_iterator base()
{
return const_iterator( m_data + 1 );
}
const T& operator[] ( size_t ind ) const
{
return ( *( m_data - ind ) );
}
bool operator==( const const_reverse_iterator& other ) const _NOEXCEPT
{
return m_data == other.m_data;
}
bool operator!=( const const_reverse_iterator& other ) const _NOEXCEPT
{
return !( other == *this );
}
bool operator<( const const_reverse_iterator& other ) const _NOEXCEPT
{
return m_data > other.m_data;
}
bool operator>( const const_reverse_iterator& other ) const _NOEXCEPT
{
return *this < other;
}
private:
const T* m_data;
};
#endif //VECTOR_CONST_REVERSE_ITERATOR_H
_NOEXCEPT
Leading underscore followed by a capitol letter. Don't do that. In fact why have it at all. We know your code needs to be at least C++11 as it has move semantics built in and C++11 has the keywordnoexcept
. \$\endgroup\$private: T *m_data; size_t m_size; size_t m_capacity;
One variable per line please." \$\endgroup\$