vector英文翻译是向量,这个名字可以说是很形象了。vector在C++中是一个可以动态增容的数组,它像string一样拥有一个随机的迭代器,支持随机访问vector的位置。vector可以说就是C语言中的顺序表,但是又有顺序表不具有的优点,比如使用了模板,支持不同类型的vector同时存在。
每写一个类,我认为应该优先考虑的就是成员变量。但是成员变量往往需要在成员函数的实现过程中确认。还好STL中有实现好的一份,我们参考一下。
namespace my_stl //因为我的vector名字和stl里相同,所以namespace封装一下 { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; private: iterator _start; //指向vector的第一个位置 iterator _finish; //指向vector的最后一个位置的下一个位置 iterator _end_of_storage; //指向vector的容量的最后一个位置的下一个位置 }; }
我们顺着stl的实现来一步一步走。较简单的只提供代码,不做讲解。
size_t size() const{ return _finish - _start; } size_t capacity() const{ return _end_of_storage - _start; } T& operator[](size_t pos){ assert(pos < size()); return _start[pos]; } const T& operator[](size_t pos) const{ assert(pos < size()); return _start[pos]; }
iterator begin(){ return _start; } iterator end(){ return _finish; } // const迭代器 const_iterator begin() const{ return _start; } const_iterator end() const{ return _finish; }
构造函数有好几个版本,只实现几个。
// 无参构造,禁止隐式类型转换 explicit vector() :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {} //一个参数n的构造函数 explicit vector(size_t n, const T& val = T()) :_start(new T[n]), _finish(_start), _end_of_storage(_start + n) { for(size_t i = 0; i < n; ++i){ *(_finish++) = val; } } //迭代器构造函数 template <class InputIterator> vector (InputIterator first, InputIterator last) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){ reserve(last - first); while(first != last){ push_back(*first); ++first; } } //initializer_list vector(initializer_list<T> il){ auto it = il.begin(); while(it != il.end()){ push_back(*it); ++it; } }
几点注意:前两个构造函数不接受隐式类型转换。后两个代码有大量重复。可以重写。
public: template <class InputIterator> vector (InputIterator first, InputIterator last) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){ reserve(last - first); range_initialize(first, last); } vector(initializer_list<T> il){ range_initialize(il.begin(), il.end()); } // 复用的部分 private: template<class InputIterator> void range_initialize(InputIterator first, InputIterator last){ while(first != last){ push_back(*first); ++first; } }
拷贝构造
// traditional 写法 vector(const vector<T>& v) :_start(new T[v.capacity()]) ,_finish(_start) ,_end_of_storage(_start + v.capacity()) { for (size_t i = 0; i < v.size(); ++i){ *(_finish++) = v[i]; } } // mordern写法 vector(const vector<T>& v) //拷贝构造现代写法 :_start(nullptr),_finish(nullptr),_end_of_storage(nullptr) { reserve(v.capacity()); //提前开好空间 for (const auto& e : v) push_back(e); } // 更mordern的写法 void swap(vector<T>& v){ ::swap(_start, v._start); ::swap(_finish, v._finish); ::swap(_end_of_storage, v._end_of_storage); } vector(const vector<T>& v) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) { vector<T> tmp(v.begin(), v.end()); swap(tmp); } // C++ 11 移动构造 vector(vector<T>&& v) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) { swap(v); }
代码中出现的未知函数的实现在下面给出。
operator=
// traditional 写法 vector<T>& operator=(const vector<T>& v){ if(this != &v){ vector<T> tmp(v); swap(tmp); } return *this; } // modern写法 pass-by-value vector<T>& operator=(vector<T> v){ if(this != &v) swap(v); return *this; } // C++ 11 移动赋值 vector<T>& operator=(vector<T>&& v){ swap(v); return *this; }
实际上移动赋值可以不用写,因为赋值的现代写法就包括了移动赋值。
析构
void clear() noexcept{ delete[] _start; _start = _finish = _end_of_storage = nullptr; } ~vector(){ clear(); }
vector是可以动态增容的数组,所以自然提供增容函数。
void reserve(size_t n){ assert(n > capacity()); size_t sz = size(); T* tmp = new T[n]; //开空间 delete[] _start; //释放旧空间 for(size_t i = 0; i < size(); ++i){ tmp[i] = _start[i]; } //拷贝数据,利用赋值 + 循环 _start = tmp; _finish = _start + sz; //这里不能加上 size(),不然有问题 _end_of_storage = _start + n; } void resize(size_t n, const T& val = T()){ if(n <= size()){ _finish = _start + n; return ; } if(n > capacity()){ reserve(n); } for(size_t i = size(); i < n; ++i){ *(_finish++) = val; } }
void push_back(const T& val){ /*if(size() == capacity()){ size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newCapacity); } *(_finish++) = val; */ insert(end(), val); } void pop_back(){ assert(size() > 0); --_finish; } iterator insert(iterator pos, const T& val = T()) //返回迭代器位置,防止迭代器失效 { size_t position = pos - _start; assert(pos >= _finish && pos <= _finish); if(size() == capacity()){ size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2; reserve(newCapacity); } iterator end = _finish; while(end >= _start + position){ *(end + 1) = *end; --end; } *(_start + position) = val; ++_finish; return _start + position; } iterator erase(iterator pos){ assert(pos >= _finish && pos <= _finish); iterator begin = pos; while(begin < _finish){ *begin = *(begin + 1); ++begin; } --finish; return pos; }
迭代器失效问题:当我们调用某些使容量变动的函数时,例如reserve,push_back,可能会引起迭代器失效。因为增容可能导致重新开辟空间,导致pos指向释放过的空间。
实际上,vector的删除操作也会导致迭代器失效。但是删除操作不会重新开辟空间。究其原因是pos迭代器对应的值被删除了,导致pos的值锁定的值变了,删除效果变了。
我们可以在使用前对迭代器重新赋值即可。
(全文完)