朋友们、伙计们,我们又见面了,本期来给大家解读一下有关vector的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!
C 语 言 专 栏:C语言:从入门到精通
数据结构专栏:数据结构
个 人 主 页 :stackY、
C + + 专 栏 :C++
Linux 专 栏 :Linux
目录
1. 基本构造
2. 容量相关的接口
2.1 operator[ ]
2.2 reserve
2.3 resize
2.4 size、capacity
3. 迭代器
4. 修改相关接口
4.1 insert、push_back
4.2 erase
5. 拷贝构造和赋值重载和其他构造
5.1 拷贝构造
5.2 赋值重载
5.3 其他构造
6. 完整代码
1. 基本构造
在实现vector之前可以先看一下库里面的vector是如何实现的:
#pragma once #include <assert.h> namespace ywh { template<class T> class vector { typedef T* iterator; typedef const T* const_iterator; public: /基本构造/// //构造 vector() {} //析构 ~vector() { delete[] _start; _start = _finish = _end_of_storage = nullptr; } private: iterator _start = nullptr; //起始 iterator _finish = nullptr; //有效数据个数 iterator _end_of_storage = nullptr; //有效空间 }; }
2. 容量相关的接口
2.1 operator[ ]
//operator[] T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; }
2.2 reserve
关于reserve的实现最主要的就是深拷贝与浅拷贝的问题,使用浅拷贝对于内置类型很容易处理,但是对于自定义类型就会出现麻烦:
需要进行深拷贝,关于自定义类型的赋值重载就是一个深拷贝,所以我们需要使用深拷贝来说实现reserve:
//reserve void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { //浅拷贝 //对于自定义类型不能处理 //memcpy(tmp,_start,sizeof(T)*sz); //采用深拷贝 for (size_t i = 0; i < sz; i++) { tmp[i] = _start[i]; } delete[] _start; } _start = tmp; _finish = _start + sz; _end_of_storage = _start + n; } }
2.3 resize
//resize //这里使用匿名对象进行缺省值的传参,由于匿名函数具有常性所以要使用const void resize(size_t n, const T& val = T()) { if (n < size()) { _finish = _start + n; } else { reserve(n); while (_finish < _start + n) { *_finish = val; ++_finish; } } }
2.4 size、capacity
有效元素的个数从开始位置到数据截止位置
容量是开始位置到空间终止位置
//容量 size_t capacity() const { return _end_of_storage - _start; } //有效个数 size_t size() const { return _finish - _start; }
3. 迭代器
关于模拟实现的vector只实现正向迭代器,反向迭代器的实现会在stack和queue的适配器模式中详解
/迭代器 iterator begin() { return _start; } iterator end() { return _finish; } const_iterator begin() const { return _start; } const_iterator end() const { return _finish; }
4. 修改相关接口
4.1 insert、push_back
在insert的时候需要注意的是:假如需要扩容,那么pos还是在旧空间,所以在扩容之前需要将旧空间pos的偏移量记录出来,然后在扩容之后将新空间的pos记录出来。
//在pos位置插入 void insert(iterator pos, const T& x) { assert(pos >= _start); assert(pos <= _finish); if (_finish == _end_of_storage) { //记录旧空间中pos的偏移量 size_t len = pos - _start; reserve(capacity() == 0 ? 4 : capacity() * 2); //找出新空间的pos pos = _start + len; } iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; --end; } *pos = x; ++_finish; } //尾插 void push_back(const T& x) { //复用插入 insert(end(), x); }
insert之后的迭代器会失效,上述中的pos就是迭代器失效的一种案例,在不同的平台下对于迭代器的失效都有不同的检查方法,VS2019上如果使用insert之后的迭代器是不允许进行使用的。
4.2 erase
//删除pos位置 void erase(iterator pos) { assert(pos >= _start); assert(pos < _finish); iterator it = pos + 1; while (it < _finish) { *(it - 1) = *it; ++it; } --_finish; }
如果我们单纯的看这个代码会感觉erase之后迭代器是不会失效的,但是erase之后迭代器失效会在特殊情况下展示出来,比如要删除偶数:
void Test() { ywh::vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(6); ywh::vector<int>::iterator it = v.begin(); while (it != v.end()) { //删除偶数 if (*it % 2 == 0) { v.erase(it); } it++; } for (auto e : v) { cout << e << " "; } cout << endl; }
在这里有三种情况:
我们可以看看库里面是如何设计的:
对于返回值的介绍:
返回被删除数据的下一个位置
所以我们需要对我们实现的erase进行调整:
//删除pos位置 iterator erase(iterator pos) { assert(pos >= _start); assert(pos < _finish); iterator it = pos + 1; while (it < _finish) { *(it - 1) = *it; ++it; } --_finish; //返回删除位置的后一个位置 return pos; }
void Test() { ywh::vector<int> v; v.push_back(2); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(6); ywh::vector<int>::iterator it = v.begin(); while (it != v.end()) { if (*it % 2 == 0) { //删除后自动找到删除位置的下一个位置 v.erase(it); } else { //不符合情况再往后面走 it++; } } for (auto e : v) { cout << e << " "; } cout << endl; }
5. 拷贝构造和赋值重载和其他构造
5.1 拷贝构造
//拷贝构造 vector(const vector<T>& v) { //开辟空间 reserve(v.capacity()); //依次尾插 for (auto e : v) { push_back(e); } }
5.2 赋值重载
void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_storage, v._end_of_storage); } vector<T>& operator=(vector<T> tmp) { //直接交换 swap(tmp); return *this; }
5.3 其他构造
//迭代器区间初始化 template<class InputIterator> vector(InputIterator first, InputIterator last) { while (first != last) { push_back(*first); ++first; } } //n个val构造 vector(size_t n, const T& val = T()) { reserve(n); for (size_t i = 0; i < n; i++) { //n个val进行尾插 push_back(val); } } vector(int n, const T& val = T()) { reserve(n); for (int i = 0; i < n; i++) { //n个val进行尾插 push_back(val); } }
6. 完整代码
头文件:Vector.h
#pragma once #include <assert.h> namespace ywh { template<class T> class vector { public: typedef T* iterator; typedef const T* const_iterator; public: /迭代器 iterator begin() { return _start; } iterator end() { return _finish; } const_iterator begin() const { return _start; } const_iterator end() const { return _finish; } /基本构造/// //构造 vector() {} //迭代器区间初始化 template<class InputIterator> vector(InputIterator first, InputIterator last) { while (first != last) { push_back(*first); ++first; } } //n个val构造 vector(size_t n, const T& val = T()) { reserve(n); for (size_t i = 0; i < n; i++) { //n个val进行尾插 push_back(val); } } vector(int n, const T& val = T()) { reserve(n); for (int i = 0; i < n; i++) { //n个val进行尾插 push_back(val); } } //拷贝构造 vector(const vector<T>& v) { //开辟空间 reserve(v.capacity()); //依次尾插 for (auto e : v) { push_back(e); } } void swap(vector<T>& v) { std::swap(_start, v._start); std::swap(_finish, v._finish); std::swap(_end_of_storage, v._end_of_storage); } //operator= vector<T>& operator=(vector<T> tmp) { //直接交换 swap(tmp); return *this; } //析构 ~vector() { delete[] _start; _start = _finish = _end_of_storage = nullptr; } ///容量 //operator[] T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; } //容量 size_t capacity() const { return _end_of_storage - _start; } //有效个数 size_t size() const { return _finish - _start; } //reserve void reserve(size_t n) { if (n > capacity()) { size_t sz = size(); T* tmp = new T[n]; if (_start) { //浅拷贝 //对于自定义类型不能处理 //memcpy(tmp,_start,sizeof(T)*sz); //采用深拷贝 for (size_t i = 0; i < sz; i++) { tmp[i] = _start[i]; } delete[] _start; } _start = tmp; _finish = _start + sz; _end_of_storage = _start + n; } } //resize //这里使用匿名对象进行缺省值的传参,由于匿名函数具有常性所以要使用const void resize(size_t n, const T& val = T()) { if (n < size()) { _finish = _start + n; } else { reserve(n); while (_finish < _start + n) { *_finish = val; ++_finish; } } } ///修改 //在pos位置插入 void insert(iterator pos, const T& x) { assert(pos >= _start); assert(pos <= _finish); if (_finish == _end_of_storage) { //记录旧空间中pos的偏移量 size_t len = pos - _start; reserve(capacity() == 0 ? 4 : capacity() * 2); //找出新空间的pos pos = _start + len; } iterator end = _finish - 1; while (end >= pos) { *(end + 1) = *end; --end; } *pos = x; ++_finish; } //尾插 void push_back(const T& x) { //复用插入 insert(end(), x); } //删除pos位置 iterator erase(iterator pos) { assert(pos >= _start); assert(pos < _finish); iterator it = pos + 1; while (it < _finish) { *(it - 1) = *it; ++it; } --_finish; //返回删除位置的后一个位置 return pos; } private: iterator _start = nullptr; //起始位置 iterator _finish = nullptr; //有效数据位置 iterator _end_of_storage = nullptr; //结束位置 }; }
朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!