Now using what we've learnt in template programming, we write a class MyVector which is a vector container similar to std::vector. The code is shown below.
template <typenameT>classMyVector {public:MyVector(int size =10) { _first =newT[size]; _last = _first; _end = _first + size; }​~MyVector() {delete[] _first; _first = _last = _end =nullptr; }​MyVector(constMyVector<T> &other) {int size =other._end -other._first; _first =newT[size];int len =other._last -other._first;for (int i =0; i < len; i++) {_first[i] =other._first[i]; } _last = _first + len; _end = _first + size; }​MyVector<T> &operator=(constMyVector<T> &other) {if (*this== other) return*this;delete[] _first;int size =other._end -other._first; _first =newT[size];int len =other._last -other._first;for (int i =0; i < len; i++) {_first[i] =other._first[i]; } _last = _first + len; _end = _first + size;return*this; }​voidpush_back(T&val) {if (full()) expand();*_last++= val; }​voidpop_back() {if (!empty()) --_last; }​T&back() const { return*(_last -1); }​boolfull() const { return _last == _end; }​boolempty() const { return _last == _first; }​intsize() const { return _last - _first; }​private: T *_first; // Start point T *_last; // One step after the last valid element T *_end; // One step after the last element in the space​voidexpand() {int size = _last - _first; T *tmp =newT[2* size];for (int i =0; i < size; i++) {tmp[i] =_first[i]; }delete[] _first; _first = tmp; _last = _first + size; _end = _first + size *2; }};
In the above class, we use three pointers pointing to the start point of the vector, one step after the last valid element, and one step after the last element in the space respectively. This container works fine when we put integers or float numbers inside, but there's something wrong with object elements. Let's look at the following case.
Here we only put three objects inside MyVector, but why 10 objects are constructed and destroyed? That is because every time we construct MyVector, we use new to create an array of size 10. Notice that new not only allocates memory on the heap, but also call the constructor of objects as well. In this case, we only need to allocate memory without constructing objects. In general, three things should be done for our previous code.
In vector constructor, memory allocation and object construction should be separated.
In vector destructor, only existing elements in the container should be destroyed and deallocated.
pop operation can't just move the pointer. Corresponding element should be destroyed as well.
To achieve this, we need the memory allocator. The memory allocator is one of the most important component of a container class. It does four things: memory allocation, memory deallocation, object construction and object destruction. Using a memory allocator, we can separate memory operations and object operations, which new and delete can not achieve.