在学习过程种 ,学习看源码是很有必要的,它可以让你更清楚的知道一些代码的细节,你也会发现源码将效率利用到了极致,不得不佩服写出源码的人。
下面我将配合源码来实现一个简单的vector,下面请看源码
看源码我们首先要看的就是成员变量,我们可以看到源码中有迭代器的start、finish、end_of_storage三个成员变量,然后可以看到它将一些类型进行重命名。我们可以将它理解为下面的这张图。
其次我们接下来看它的构造函数,它提供了无参和带参的构造函数,看到有fill_initialize这个函数,不难猜一猜可能和初始化有关。这里就不将它展开说了,有兴趣的可以去看看《STL源码剖析》这本书。
学习了vector,可以知道resize是一个改变vector的finish
- 如果传入new_size比有效元素少,就将后面的元素删除
- 如果传入的new_size比有效元素多,那么就在最后加上x
与resize相对应的就是reserve函数了,它是改变vector的end_of_storage
- 如果开辟的容量大于end_of_storage,那么就开辟一个n个空间的大小
- 然后将数据拷贝到新空间中,然后将旧空间释放掉
- 最后改变start的指向
但是这里注意一下,当发生数据拷贝的时候,是不能直接进行memcpy,要知道vector中存放的是一个T类型,如果T是string,那么当发生memcpy的时候就会发生浅拷贝,里面string指向的是一块释放了的空间。
接下来我们看一下insert函数
- 关于insert,首先我们要知道当进行插入操作时就有可能触发扩容,那么就有可能触发reserve函数,那么就有可能空间释放的问题,所以不能memmove。
所以,了解了一些特殊情况,就可以实现了。
template <class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector(): start(nullptr), finish(nullptr), end_of_storage(nullptr){}vector(size_t n, const T &value){push_back(value);}explicit vector(size_t n){for (int i = 0; i < n; i++){push_back(T());}}template<class InputIterator>vector(InputIterator first,InputIterator last){while(first != last){push_back(*first);++first;}}vector(int n, const T &value = T()){resize(n, value);}vector(long n, const T &value = T()){resize(n, value);}// v2(v1)vector(const vector<T> &v){reserve(v.capacity());for (auto e : v){push_back(e);}}//v2 = v1vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (start){delete[] start;start = finish = end_of_storage = nullptr;}}iterator begin(){return start;}const_iterator begin() const {return start;}iterator end(){return finish;}const_iterator end() const {return finish;}size_t size() const{return finish - start;}size_t max_size() const{return size_t(-1) / sizeof(T);}bool empty() const {return start == finish;}void swap(vector<T> &v){::swap(start, v.start);::swap(finish, v.finish);::swap(end_of_storage, v.end_of_storage);}void resize(size_t n, T val = T()){// 如果n > size()就开空间,将val插入到后面if (n > size()){reserve(n);while (finish < start + n){*finish = val;++finish;}}else{// 将有效空间变小finish = start + n;}}void reserve(size_t n){if (n > capacity()){size_t old = size();// 申请新空间T *tmp = new T[n];if (start){// memcpy(tmp,start,sizeof(T)*old);// 之所以不能用memcpy,是因为如果是自定义类型,深拷贝的时候就会访问释放了的空间了// 当然,这里可以用引用计数或者移动构造、移动赋值来搞定这个问题。for (size_t i = 0; i < old; i++){tmp[i] = start[i];}delete[] start;}start = tmp;finish = start + old;end_of_storage = start + n;}}size_t capacity() const{return end_of_storage - begin();}void push_back(const T &x){if (finish != end_of_storage){*finish = x;++finish;}else{size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);*finish = x;++finish;}}void pop_back(){assert(size() > 0);--finish;}iterator erase(iterator pos){assert(pos >= start);assert(pos < finish);// 将pos之后的元素从后往前移动iterator it = pos + 1;while (it < finish){*pos = *it;it++;}--finish;return pos;}iterator insert(iterator pos,const T& x){assert(pos <= finish && pos >= start);if(finish == end_of_storage){size_t len = pos - start;reserve(capacity() == 0 ? 4 : 2*capacity());pos = start + len;}iterator end = finish;while(pos < end){*end = *(end-1);--end;}*pos = x;++finish;return pos;}T &operator[](size_t n){assert(n <= size());return *(begin() + n);}const T &operator[](size_t pos) const {assert(pos >= start);assert(pos < finish);return start[pos];}private:iterator start = nullptr;iterator finish = nullptr;iterator end_of_storage = nullptr;};