先看一下官方文档中对构造函数是怎么描述的。
这里的allocator什么的表示内存池,能够更快的申请空间,这次的实现就不实现内存池了,直接new。
首先要先声明自己的命名空间
namespace mudan { class vector { }; }
为什么这里我叫mudan呢?因为我媳妇叫牡丹,哈哈哈~
然后可以看到参数当中有很多都是没有听过的,一般都是typedef的,可以去查一下Member types
可以看到iterator其实就是模板T。
namespace mudan { template<class T> class vector { public: typedef T* iterator; private: iterator _start;//起始位置 iterator _finish;//最后一个元素的后一个位置 iterator _end_of_capacity;//整个容器的最后一个位置的下一个位置 }; }
成员定义好了就该实现构造函数了。
默认构造函数/析构函数
vector() :_start(nullptr) ,_finish(nullptr) ,_end_of_capacity(nullptr) {} //区间构造函数 template<class InputIterator> vector(InputIterator first, InputIterator last) :_start(nullptr) ,_finish(nullptr) ,_end_of_capacity(nullptr) { while (first != last) { push_back(*first); first++; } } ~vector() { delete[] _start; _finish = nullptr; _end_of_capacity = nullptr; }
直接初始化为nullptr即可,因为随机的空间是不能被操作的。
可以看到初始化成功了。
然后就可以开始尝试实现push_back函数了。
而这里要实现push_back分为几种情况
1.原来的空间为nullptr就需要分配内存空间,一般先分配四个大小的空间
2.空间不够需要扩容,一般扩大到原空间的两倍较为合适
3.直接添加元素
这里需要扩容就需要实现一下reserve函数了。
而在reserve函数当中需要得知当前的容量大小,因此,可以实现一下size()和capacity()。
size()/capacity() size_t size() { return (_finish-_start); } size_t capacity() { return (_end_of_capacity-_start); }
reserve()
void reserve(size_t n) { if (n > capacity()) { T* tmp = new T[n];//开辟一个n个大小的空间 if (_start) { memcpy(tmp, _start, sizeof(T) * capacity());//将原来空间的内容拷贝过来 } delete[] _start; } _start = tmp; _finish = _start + sz; _end_of_capacity = _start + n; } 1
push_back()
void push_back(const T &val) { if (_finish == _end_of_capacity) { reserve(capacity() == 0 ? 4 : 2 * capacity()); } *_finish = val; ++_finish; }
好了,现在可以简单的测试一下程序对不对了。
开搞!!!
咦,程序怎么崩溃了?还是报的空指针问题?
通过调试可以发现问题发生在扩容这里了。
程序走到这里都是很正常的,空间也都开辟了
但走到下一步的时候,size()传回的值确实一个超级大的数字,这是为啥?
因为_start确实有空间了,但是_finish还没有啊,而size()的实现方式是通过_finish-_start所得到的,当然会出现问题,所以这里问题就出现在size()上了。
解决方法就是提前算好size()的大小。
reserve()修改 void reserve(size_t n) { size_t sz = size(); if (n > capacity()) { T* tmp = new T[n];//开辟一个n个大小的空间 if (_start) { memcpy(tmp, _start, sizeof(T) * sz);//将原来空间的内容拷贝过来 } delete[] _start; _start = tmp; _finish = _start + sz; _end_of_capacity = _start + n; } }
为了进一步验证正确性,实现一下operator[]
operator[] reference就是T& T& operator[](size_t pos) { assert(pos < size()); assert(pos >= 0); return _start[pos]; } const T& operator[](size_t pos)const { assert(pos < size()); assert(pos >= 0); return _start[pos];
为什么库里要实现两个operator[]?
因为有时候传过来的是const类型,因为返回是引用类型,所以会有权限的放大的问题,因此const对应const的operator就可以了。
可以看到程序是正常运行的。
pop_back() void pop_back() { _finish--; }
为了支持范围for,实现一个begin()和end(),因为范围for是很简单的替换,只认begin()和end()。
begin()/end() const_iterator begin()const { return _start; } const_iterator end()const { return _finish; } iterator begin() { return _start; } iterator end() { return _finish; }
然后就是利用迭代器进行遍历
如果这里的iterator不是在public当中typedef的就会报错,因为会受到访问限定符的约束。
insert()
在position位置之前插入一个val。
根据以前实现的顺序表可以知道,如果要在1 2 3 4 5 6当中的3之前插入一个520,需要将3 4 5 6全都往后挪动一个位置,空出一个位置给520–>1 2 520 3 4 5 6。
void insert(iterator pos, const T& val) { assert(pos >= _start); assert(pos <= _finish); if (_finish == _end_of_capacity) { reserve(capacity() == 0 ? 4 : 2 * capacity()); } iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; end--; } *pos = val; ++_finish; }
为了测试代码的正确性,可以测试一下这个程序------>在所有的偶数之前插入一个当前偶数的二倍。
但走着走着怎么程序就挂掉了呢?并且出现了随机数。
其实跟着之前的代码走一遍就知道哪里出错了。
首先,第一个问题就是it没有++会导致一直都是在第二个位置进行插入,再一个问题就是当容量不够的时候是需要扩容的,这里的扩容不是原地扩容,所以会导致野指针的问题,即pos的指向已经不是原来的内容了。
我们回到库当中的insert的定义:
可以看到还给了一个迭代器版本的insert,这里就可以利用这个迭代器版本的insert的返回值来更新it,让it动起来并且会更新it的地址,当遇到不是偶数的时候直接++即可,返回值就是更新后的pos位置,为了防止迭代器失效可以在每次扩容之后更新一下pos的位置,不过要先计算好pos位置离_start的距离是多少。
iterator insert(iterator pos, const T& val) { assert(pos >= _start); assert(pos <= _finish); if (_finish == _end_of_capacity) { size_t len = pos - _start; reserve(capacity() == 0 ? 4 : 2 * capacity()); pos = _start + len;//更新迭代器位置,防止迭代器失效 } iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; end--; } *pos = val; ++_finish; return pos+1; } ====================================================================================== while (it != arr.end()) { if ((*it) % 2 == 0) { it=arr.insert(it, (*it) * 2); } it++; } for (auto e : arr) { cout << e << " "; }
目前来看都是正常的。
然后就是实现一下erase了。
erase()
erase()也是和之前写过的顺序表一样,直接向前覆盖就行了。
假如要实现一个删除所有奇数的程序,利用erase()实现:
iterator erase(iterator pos) { assert(pos >= _start); assert(pos <_finish); iterator begin = pos + 1; while (begin < _finish) { *(begin - 1) = *begin; begin++; } --_finish; return pos; }
operator= vector<T>& operator=(const vector<T> v) { swap(v); return *this; }
swap void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_capacity, v._end_of_capacity); }
resize() void resize(size_t n,const T&val=T()) { if (n > capacity()) { reserve(n);//大于直接扩容 } if (n > size()) { //初始化并且填上值 while (_finish < _start + n) { *_finish = val; ++_finish; } } else { _finish = _start + n; } }
现在,vector的常用接口基本都实现好了,做一道杨辉三角的题目试试。
class Solution { public: vector<vector<int>> generate(int numRows) { vector<vector<int>> ret(numRows); for (int i = 0; i < numRows; ++i) { ret[i].resize(i + 1); ret[i][0] = ret[i][i] = 1; for (int j = 1; j < i; ++j) { ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1]; } } return ret; } };
这是使用官方的库实现的。
但使用我们自己的突然程序就崩溃了。
可以看到,是在析构函数崩溃的,可以先猜想,是不是因为同一块内存空间析构了两次导致的呢?
这里的第二个_start是浅拷贝。
等_start析构完了,tmp也变成也指针了,但最后tmp还要析构一次,所以会导致一块内存析构两次的结果。
reserve()修改 void reserve(size_t n) { if (n > capacity()) { size_t oldSize = size(); T* tmp = new T[n]; if (_start) { //memcpy(tmp, _start, sizeof(T)*oldSize); for (size_t i = 0; i < oldSize; ++i) { tmp[i] = _start[i]; } delete[] _start; } _start = tmp; _finish = tmp + oldSize; _end_of_capacity = _start + n; } }
最后发现还是跑不了,靠,原来是忘记写拷贝构造了,默认的浅拷贝还是会导致同一块内存析构两次。
拷贝构造
vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) { vector<T> tmp(v.begin(), v.end()); swap(tmp); } template<class InputIterator> vector(InputIterator first, InputIterator last) : _start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) { while (first != last) { push_back(*first); first++; } } vector(size_t n, const T& val = T()) :_start(nullptr) ,_finish(nullptr) ,_end_of_capacity(nullptr) { reserve(n); for (size_t i = 0; i < n; ++i) { push_back(val); } } vector(int n, const T& val = T()) :_start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) { reserve(n); for (int i = 0; i < n; ++i) { push_back(val); } }
现在程序就没啥问题咯。
总代码
#pragma once #include<assert.h> #include<algorithm> #include<iostream> using namespace std; namespace mudan { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; //默认构造函数 vector() :_start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) {} template<class InputIterator> vector(InputIterator first, InputIterator last) : _start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) { while (first != last) { push_back(*first); first++; } } vector(size_t n, const T& val = T()) :_start(nullptr) ,_finish(nullptr) ,_end_of_capacity(nullptr) { reserve(n); for (size_t i = 0; i < n; ++i) { push_back(val); } } vector(int n, const T& val = T()) :_start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) { reserve(n); for (int i = 0; i < n; ++i) { push_back(val); } } vector(const vector<T>& v) :_start(nullptr) , _finish(nullptr) , _end_of_capacity(nullptr) { vector<T> tmp(v.begin(), v.end()); swap(tmp); } //析构函数 ~vector() { delete[] _start; _finish = nullptr; _end_of_capacity = nullptr; } size_t size() { return (_finish - _start); } size_t capacity() { return (_end_of_capacity - _start); } const_iterator begin()const { return _start; } const_iterator end()const { return _finish; } iterator begin() { return _start; } iterator end() { return _finish; } void reserve(size_t n) { if (n > capacity()) { size_t oldSize = size(); T* tmp = new T[n]; if (_start) { //memcpy(tmp, _start, sizeof(T)*oldSize); for (size_t i = 0; i < oldSize; ++i) { tmp[i] = _start[i]; } delete[] _start; } _start = tmp; _finish = tmp + oldSize; _end_of_capacity = _start + n; } } void push_back(const T &val) { if (_finish == _end_of_capacity) { reserve(capacity() == 0 ? 4 : 2 * capacity()); } *_finish = val; ++_finish; } void pop_back() { _finish--; } iterator insert(iterator pos, const T& val) { assert(pos >= _start); assert(pos < _finish); if (_finish == _end_of_capacity) { size_t len = pos - _start; reserve(capacity() == 0 ? 4 : 2 * capacity()); pos = _start + len; } iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; end--; } *pos = val; ++_finish; return pos+1; } iterator erase(iterator pos) { assert(pos >= _start); assert(pos < _finish); iterator begin = pos + 1; while (begin < _finish) { *(begin - 1) = *begin; begin++; } --_finish; return pos; } T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; } T& front() { assert(size() > 0); return *_start; } T& back() { assert(size() > 0); return *(_finish - 1); } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_capacity, v._end_of_capacity); } vector<T>& operator=(vector<T> v) { swap(v); return *this; } void resize(size_t n, T val = T()) { if (n > capacity()) { reserve(n); } if (n > size()) { while (_finish < _start + n) { *_finish = val; ++_finish; } } else { _finish = _start + n; } } private: iterator _start;//起始位置 iterator _finish;//最后一个元素的后一个位置 iterator _end_of_capacity;//整个容器的最后一个位置的下一个位置 }; class Solution { public: vector<vector<int>> generate(int numRows) { vector<vector<int>> vv; vv.resize(numRows, vector<int>()); for (size_t i = 0; i < vv.size(); ++i) { vv[i].resize(i + 1, 0); vv[i][0] = vv[i][vv[i].size() - 1] = 1; } for (size_t i = 0; i < vv.size(); ++i) { for (size_t j = 0; j < vv[i].size(); ++j) { if (vv[i][j] == 0) { vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1]; } } } for (int i = 0; i < vv.size(); i++) { for (int j = 0; j < vv[i].size(); j++) { cout << vv[i][j] << " "; } cout << endl; } return vv; } }; void test() { vector<int>arr; arr.push_back(1); arr.push_back(2); arr.push_back(3); arr.push_back(4); arr.push_back(5); arr.push_back(6); arr.push_back(7); arr.push_back(8); arr.push_back(9); arr.push_back(10); for (int i = 0; i < arr.size(); i++) { cout << arr[i] << " "; } cout << endl; for (auto e : arr) { cout << e << " "; } cout << endl; cout << "============================================" << endl; //vector<int>::iterator it = arr.begin(); //while (it != arr.end()) //{ // if ((*it) % 2 == 0) // { // it=arr.insert(it, (*it) * 2); // } // it++; //} //for (auto e : arr) //{ // cout << e << " "; //} cout << endl; vector<int>::iterator ite = arr.begin(); while (ite != arr.end()) { if ((*ite) % 2 != 0) { ite = arr.erase(ite); } else { ite++; } } for (auto e : arr) { cout << e << " "; } cout << endl; vector<vector<int>>vv; } }