Vector底层实现
vector的三个私有成员
:_start 记录初始位置
, _finish 记录有效字符
, _endofstoage 记录容量大小
vector会存储的类型不同,所以要用模版来定类型
typedef T* iterator;
iterator _start;
iterator _finish;
iterator _endofstoage;
也就是T*
构造函数的方法很多 可以用迭代器的范围来构造
//用迭代器构造的构造函数
传过来的是它的迭代器的类型 我们也用它的类型来接收 不比加* &
三个属性先初始化
只要根据传过来的范围来push_back()即可
push_back函数后面会实现
//用迭代器构造的构造函数 template <class InputIterator> vector(InputIterator first, InputIterator last) : _start(nullptr) , _finish(nullptr) , _endofstoage(nullptr) { while (first != last) { push_back(*first); ++first; } }
构造是 也可以根据n分val来构造 所以这个功能也需要提供
传过来的是n个 用size_t接收 因为n必须是>=0的 而val是根据类型 所以用模版类型接受
有些情况下 val会不传参 那么我们就会提供他的默认构造 (注意 在C++中,内置类型也是有默认构造的)
三个属性初始话
先用reserve函数创建n个空间
在分别push_back()添加val
//构造n个val的构造函数 vector(size_t n, const T& val = T()) : _start(nullptr) , _finish(nullptr) , _endofstoage(nullptr) { reserve(n); for (size_t i = 0; i < n; ++i) { push_back(val); } }
因为某些情况 第一个参数是int 第二个参数也是int 会调用到迭代器的函数 因为这两个类型更加适配,所以会出问题,所以需要再提供一个第一个参数为int的相同函数,来避免这种情况
//构造n个val的构造函数 //因为用int会调用到其他函数 所以为了区分 单独写出一个第一个为int vector(int n, const T& val = T()) : _start(nullptr) , _finish(nullptr) , _endofstoage(nullptr) { reserve(n); for (int i = 0; i < n; ++i) { push_back(val); } }
swap函数
这个函数是用来给拷贝构造使用
交换类的三个属性成员
//交换 void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_endofstoage, v._endofstoage); }
拷贝构造
拷贝的本质是把一个有数据的拷贝给一个无数据的,只需要用这个无数据的迭代器去调用迭代器构造,给一个临时tmp,最后再用这个无数据的与临时tmp交换 即可
因为传过来的是另一个vector 所以必须用vector<T> 接收 C++传参是特别需要注意的,真的很容易乱
//拷贝构造 vector(const vector<T>& v) : _start(nullptr) , _finish(nullptr) , _endofstoage(nullptr) { vector<T> tmp(v.begin(), v.end());//复用用迭代器构造的构造函数 swap(tmp); }
赋值重载
v1=v2 是两个vector的数据赋值 所以它的返回值必须是vector<t> 而传参数时 也必须是vector<t>
函数本质也是交换,所以直接调用swap 这里之所以能直接调用 而不影响到v2 是因为函数是用传值传参,它是不会影响到v2本体的,(现代写法)
返回时是返回本体 (*this)
vector<T>& operator=(vector<T> v)//赋值重载 不用引用 现代写法 { swap(v);//现代写法 return *this; }
析构函数
判断是否为空 当不为空时才需要析构 如果为空 去析构 会崩溃
把数据释放,并且它三个属性置空
// 资源管理 ~vector() { if (_start) { delete[] _start; _start = _finish = _endofstoage = nullptr; } }
迭代器部分
vector的迭代器本质就是指针 根据传进来的类型 iterator就是这个类型的指针
并且迭代器分为const版本和非const版本
所以需要提供两个版本
//迭代器 typedef T* iterator; typedef const T* const_iterator;
注意 end返回的是你的实际有效字符 而不是你的的空间多大
iterator begin() { return _start; } iterator end() { return _finish; } const_iterator begin() const { return _start; } const_iterator end() const { return _finish; }
size
你的有效字符 _finish 而_finish实际上是有效字符的下一个位置
所以需要减去初始的位置 得到它真正的有效字符
size_t size() const { return _finish - _start; }
capacity
计算的是vector的空间大小 也就是你的记录空间大小减去初始位置
size_t capacity() const { return _endofstoage - _start; }
【】重载
vector是支持随意访问的,所以【】的重载必不可少
返回的是vector里存储的类型 实际上就是模版类型&nb