从零开始的C++(十一)
vector的模拟实现:
1.构造函数:
vector(){}vector(int n, const T& value = T()){ reserve(n);for (int i = 0; i < n; i++){push_back(value);}}template<class InputIterator>vector(InputIterator first, InputIterator last){ auto it = first;while (it != last){push_back(*it);it++;}}void swap(iterator&v1,iterator&v2){iterator ret = v1;v1 = v2;v2 = ret;}vector(const vector<T>& v){vector<T>tmp(v.cbegin(), v.cend());swap(_start,tmp._start);swap(_finish, tmp._finish);swap(_endOfStorage, tmp._endOfStorage);}
private:iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾
对象的成员使用了参数列表,防止随机值引发异常。同时,对于传同类型对象的构造函数,复用了传迭代器的构造函数创建临时对象,然后交换this所指对象和临时对象的成员的值,防止浅拷贝,并且利用临时变量销毁原本this对象的成员的内容。
2.赋值重载:
vector<T>& operator= (vector<T> v){swap(_start, v._start);swap(_finish, v._finish);swap(_endOfStorage, v._endOfStorage);return *this;}
赋值重载的原理和拷贝构造类似,都是用临时对象交换赋值,此处不用创建临时对象的原因是在实参到形参的过程已经进行了拷贝构造,即此处形参就是临时对象,所以直接交换即可。
3.插入和删除
void push_back(const T& x){if (size() == capacity()){reserve(size() * 2+1);}*(_finish) = x;_finish++;}void pop_back(){assert(size() > 0);_finish--;}void swap(vector<T>& v){swap(_start, v._start);swap(_finish, v._finish);swap(_endOfStorage, v._endOfStorage);}iterator insert(iterator pos, const T& x){ assert(pos-_start <= size());assert(pos - _start>= 0);if (size() == capacity()){ int sz = pos - _start;reserve(size() * 2+1);pos = _start + sz;}auto it = _finish-1;while (it >= pos){*(it + 1) = *it;it--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){ assert(pos-_start >= 0);assert(pos-_start < size());auto it = pos+1;while (it != _finish){*(it - 1) = *(it);it++;}--_finish;return pos;}
需要注意的是,insert和erase都可能会造成迭代器失效(即迭代器使用结果可能未定义或无法使用)。同时也需要注意判断插入、删除是否合理(是否越界等)。
4.扩容
void reserve(size_t n){if (n >capacity()){ int sz = size();iterator tmp = new T[n];if (_start!=nullptr){//值拷贝for (int i = 0; i < sz; i++){tmp[i] = _start[i];}}delete[]_start;//更新_start = tmp;_endOfStorage = _start + n;_finish = _start + sz;}}void resize(size_t n, const T& value = T()){if (n <=size()){_finish = _start + n;}else{reserve(n);while (size() < n){push_back(value);}}}
扩容应注意是深拷贝,因为成员可能是自定义类型,有自己的析构函数,如果是浅拷贝可能会出现二次delete的情况,也可能会出现析构导致内容变成随机值无法正常使用。