I have attempted to implement a similar version of the STL Vector; several functions are missing but I'd like a few words of advice on whether I am indeed on the right track or whether I should change my approach. My goal here is to understand how the vector works behind the scenes, rather than using it for my applications.
#include <initializer_list>
#include <algorithm>
#include <memory>
struct out_of_range {};
template<typename T, typename A>
struct vector_base
{
A alloc; //allocator
T* elem; //start of allocation
int sz; //number of elements
int space; //amount of allocated space
vector_base(int n)
:elem{alloc.allocate(n)}, sz{n}, space{n} {}
void construct(T def) {for (int i = 0; i < sz; i++) alloc.construct(&elem[i], def); }
~vector_base()
{
//for(int i = 0; i < sz; i++) alloc.destroy(&elem[i]);
alloc.deallocate(elem, space);
std::cout << "Vector destroyed.\n";
}
};
template<typename T, typename A = std::allocator<T>> //requires Element<T>()
class vector : private vector_base<T,A>
{
void reserve(int newspace);
public:
vector (int size, T def = T())
: vector_base<T,A>(size)
{
this->construct(def); // initialize each element to 0
std::cout << "Vector constructed\n";
}
//~vector() {delete [] this->elem;}
//{} initialization
vector (std::initializer_list<int> lst)
:vector_base<T,A>(lst.size())
{
std::copy (lst.begin(), lst.end(), this->elem);
std::cout << "Vector constructed\n";
}
//destruction of elements is managed in vector_base
vector (const vector& arg); //copy constructor
vector& operator= (const vector& arg); //copy assignment
vector ( vector&& arg); //move constructor
vector& operator= (vector&& arg); //move assignment
//subscript operators
T& operator[] (int n) { return this->elem[n]; }
const T& operator[] (int n) const { return this->elem[n]; }
T& at(int n);
const T& at(int n) const;
int size() const { return this->sz; }
int capacity() const { return this->space; }
void resize(int newspace, T def = T()); //growth
void push_back(const T& d);
void erase();
};
//copy constructor
template<typename T,typename A>
vector<T,A>::vector (const vector<T,A>& arg)
:vector_base<T,A>{arg.size()}
{
std::copy (arg.elem, arg.elem + arg.sz, this->elem);
std::cout << "Vector constructed\n";
}
//copy assignment
template<typename T,typename A>
vector<T,A>& vector<T,A>::operator= (const vector<T,A>& arg)
{
if (this == &arg) return *this; //self-assignment, do nothing
if (arg.sz <= this->space) // enough space, no need for extra allocation
{
for (int i = 0; i < arg.sz; i++) this->elem[i] = arg.elem[i]; //copy elements
this->sz = arg.sz;
return *this;
}
T* n = this->alloc.allocate(arg.sz);
for (int i = 0; i < arg.sz; i++) n[i] = arg.elem[i]; //copy all the elements from a to the newly allocated array
//for (int i = 0; i < arg.sz; i++) this->alloc.destroy(&this->elem[i]);
this->alloc.deallocate(this->elem, this->space);
this->space = arg.space;
this->sz = arg.sz;
this->elem = n; //set the elem (of the current vector) to the argumen's elem
return *this; //return a self-reference
}
template<typename T, typename A>
T& vector<T,A>::at(int n)
{
if (n < 0 || n >= this->sz) throw out_of_range();
return this->elem[n];
}
template<typename T, typename A>
const T& vector<T,A>::at(int n) const
{
if (n < 0 || n >= this->sz) throw out_of_range();
return this->elem[n];
}
//move constructor
template<typename T,typename A>
vector<T,A>::vector (vector<T,A> && arg)
:vector_base<T,A>{arg.size()}
{
this->elem = arg.elem;
arg.elem = nullptr;
arg.space = 0;
arg.sz = 0;
std::cout << "Vector constructed\n";
}
//move assignment
template<typename T,typename A>
vector<T,A>& vector<T,A>::operator= (vector<T,A> && arg)
{
this->elem = arg.elem;
this->sz = arg.sz;
for (int i = 0; i < arg.sz; i++) this->alloc.destroy(&this->elem[i]);
arg.elem = nullptr;
arg.sz = 0;
return *this;
}
template<typename T,typename A>
void vector<T,A>::reserve(int newspace)
{
if(newspace <= this->space) return; //never allocate less memory
T* p = this->alloc.allocate(newspace);
for(int i = 0; i < this->sz; i++) this->alloc.construct(&p[i], this->elem[i]); //copy the elements from the old array to the new one
//for(int i = 0; i < this->sz; i++) this->alloc.destroy(&this->elem[i]);
this->alloc.deallocate(this->elem, this->space);
this->elem = p; //point elem to the newly allocated array p
this->space = newspace;
}
template<typename T,typename A>
void vector<T,A>::resize(int newspace, T def)
//make the vector have newspace elements
//initialize each new element with a default value
// -default T value
// -or a value d if specified
{
reserve(newspace);
for (int i = this->sz; i < newspace; i++) this->alloc.construct(&this->elem[i],def);
for (int i = newspace; i < this->sz; i++) this->alloc.destroy(&this->elem[i]);
this->sz = newspace;
}
template<typename T,typename A>
void vector<T,A>::push_back(const T& d)
{
if(this->space == 0) //if there's no space (default constructor)
reserve(8); //reserve space for 8 elements
else if (this->sz == this->space) //if the space is equal to the size
reserve(this->space * 2); //double the space
this->alloc.construct(&this->elem[this->sz], d); // add d at the end
++this->sz; // increase size by 1
}