C++ :vector的模拟
目录
一、vector的迭代器
二、vector的构造函数
默认构造函数
参数构造函数
迭代器范围构造函数
拷贝构造函数
swap: 交换vector
重载赋值符
析构函数
reserve : 扩容vector
resize : 调整大小
push_back : 添加元素
empty : 判空
pop_back : 后删
获取大小与容量 : size() , capacity()
重载operator[] : 元素访问
insert : 插入元素
erase:删除一个元素
类的数据成员:
自定义 vector 类的实现
总结
在实际应用中,建议使用标准库stl提供的容器,因为它们经过了优化和充分测试。然而,深入了解vector内部实现无疑会使你加深对 vector 的理解,还能培养解决复杂编程问题的能力。
一、vector的迭代器
template <class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;//typedef const T* iterator const;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}
- begin() : 用于返回空间的首地址
- end() : 用于返回空间的尾部的下一个地址
- begin() const : 返回指向常量版本的vector开始地址
- begin() const : 返回指向常量版本的vector尾部地址
二、vector的构造函数
默认构造函数
vector(){}
- 默认构造函数不执行任何操作,初始化时所有指针均为
nullptr
参数构造函数
vector(size_t n,const T& val=T())
{resize(n, val);
}
vector(int n, const T& val = T())
{resize(n, val);
}
- 把size_t 和 int 类型的参数分开是为了区分
////vector<int> v(10,1);10 是int 型更倾向于 InputIterator first 而不是size_t n;
template <class InputIterator>
vector(InputIterator first,InputIterator last)
- 该构造函数创建一个包含
n
个元素的vector
,每个元素都被初始化为val
迭代器范围构造函数
//[ first , last )
template <class InputIterator>
vector(InputIterator first,InputIterator last)
{while (first != last){push_back(*first);first++;}
}
- 构造函数接受迭代器first 和 last,依次将区间内的元素添加到
vector
中。 - 范围是 [first , last)
拷贝构造函数
vector(const vector<T>& v)//拷贝构造
{//way one:_start = new T[v.capacity()];for (size_t i = 0; i < se; i++){_start[i] = v._start[i];}_finish = _start + v.size();_end = _start + v.capacity();//way two:reserve(v.capacity());for (auto e : v){push_back(e);}}
- 有两种方式
swap: 交换vector
void swap(vector<T>& v)
{std::swap(_start, v.start);std::swap(_finish, v._finish);std::swap(_end, v._end);
}
- 交换当前容器与另一个容器的内部指针
重载赋值符
vector<T>& operator=(vector<T> v)//v相当于tmp
{/*vector v1 = vector(v);return v1;*/swap(v);return *this;
}
- v是通过深拷贝来的相当于一个临时值tmp
析构函数
~vector()
{if (_start){delete[] _start;_start = _finish = _end = nullptr;}
}
- 释放分配的内存,并将所有指针设置为
nullptr
,防止野指针
reserve : 扩容vector
void reserve(size_t n)
{if (n > capacity()){ size_t se = size();//要先保存size(),确保_finish 的位置T* tmp = new T[n];if (_start) {/*memcpy(tmp, _start, sizeof(T) * se);delete[] _start;*/ for (size_t i = 0; i < se; i++){tmp[i] = _start[i];}delete[]_start;}_start = tmp;//错误:_finish=_start+size();//size()是_finish-_start 而此时_start 已经改变_finish = _start + se;//所以要先保存原先的size()_end = _start + n;//_start 和_finish 和_end 都是 指针指向地址}
}
- reserve扩容是只扩不缩的
- 由于我们要进行扩容的时候默认采取异地扩容,所以当进行异地扩容的时候,_start指向的空间必然会被释放,并且_start指向新空间,那么在调用size的时候获取的当前元素的有效个数大小就必然会不正确,所以在进行正式扩容之前,我们提前使用sz变量保存一下当前元素的有效个数
- 使用tmp指向使用new[]申请n个T类型的大小的空间,接下来就是拷贝_start指向空间上的数据到tmp指向的空间上 使用 memcpy函数 进行按字节数进行拷贝数据,是浅拷贝,当要拷贝的数据是内置类型的时候进行按字节进行拷贝不会出错,但是如果要拷贝的数据类型是需要进行深拷贝的自定义类型的时候就会出错,而用for循环进行深拷贝不会出错
resize : 调整大小
//万能初始值
void resize(size_t n,const T& val=T())//val默认值怎么给---val接收T 类型的匿名对象 在这里内置类型也有(默任构造)函数 (前提是有模版)
{ // int i=0; int j=int(); int k=int(1); i--0 j--0 k--1if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n)//目的是为了初始化{*_finish = val;_finish++;}}
}
- 如果 n 大于当前大小,则增加元素,并使用
val
初始化新增部分 - 如果 n 小于当前大小,则通过调整
_finish
指针减少有效元素的数量 - val默认值怎么给? ---val接收T 类型的匿名对象 在这里内置类型也有(默任构造)函数 (前提是有模版) 代码演试 : // int i=0; int j=int(); int k=int(1); i==0 , j==0 , k==1
push_back : 添加元素
void push_back(const T& x)
{if (_finish == _end){size_t neew = capacity() == 0 ? 4 : 2 * capacity();reserve(neew);}*_finish = x;_finish++;//insert(end(), x);
}
- 两种方法更推荐//insert(end(), x);
empty : 判空
bool empty()
{return _size == 0;
}
-
empty()
判断容器是否为空
pop_back : 后删
void pop_back()//后删
{assert(!empty());erase(--end());
}
获取大小与容量 : size() , capacity()
size_t capacity()const
{return _end - _start;
}size_t size()const
{return _finish - _start;
}
size()
返回当前有效元素的数量。capacity()
返回当前分配的存储容量。
重载operator[] : 元素访问
T& operator[](size_t n)
{assert(n < size() && n >= 0);return _start[n];
}
const T& operator[](size_t n) const
{assert(n < size()&&n >= 0);return _start[n];
}
- 提供了对元素的快速随机访问像访问数组一样
insert : 插入元素
iterator insert(iterator pos, const T& x)//每个位置都是指针 iterator
{assert(pos >= _start&&pos<=_finish);if (_finish ==_end){size_t len = pos - _start;//记录一下pos的位置 pos之后有可能发生改变size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;//开空间的时侯,pos 有可能销毁掉,所以要换空间}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;
}
- assert检查插入位置的有效性。
- if判断存储空间,不足则调用
reserve
增加容量。 - 从pos位置开始,将后续元素向后移动一个位置,留出插入的位置。
- 将 x 放置在插入位置,并返回指向新元素的迭代器。
erase:删除一个元素
iterator erase(iterator pos)
{assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish)//数据往后覆盖掉pos{*(it - 1) = *it;it++;}_finish--;return pos;
}
- 数据往后覆盖掉pos
类的数据成员:
private:iterator _start=nullptr;iterator _finish=nullptr;iterator _end=nullptr;
_start
:指向空间的起始位置地址_finish
:指向有效数据的下一个位置的地址_end
:指向分配存储空间的末尾的地址,标志着存储的容量界限。
( 小编写代码的时侯为了方便把 _end_of_storge 换成了 _end ,不要学小编)
自定义 vector
类的实现
自己实现的vector类会和库里的vector重名,所以我们在vector.h中开辟一个我们自己的命名空间,在这个命名空间中用于我们vector的模拟实现
namespace TXF
{template <class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//typedef const T* iterator const;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;}//vector<int> v(10,1);10 是int 型更倾向于 InputIterator first 而不是size_t n;vector(size_t n,const T& val=T()){resize(n, val);}vector(int n, const T& val = T()){resize(n, val);}//[ first , last )template <class InputIterator>vector(InputIterator first,InputIterator last){while (first != last){push_back(*first);first++;}}vector(){}vector(const vector<T>& v)//拷贝构造{//way one:/*_start = new T[v.capacity()];for (size_t i = 0; i < se; i++){_start[i] = v._start[i];}_finish = _start + v.size();_end = _start + v.capacity();*///way two: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, v._end);}vector<T>& operator=(vector<T> v)//v相当于tmp{/*vector v1 = vector(v);return v1;*/swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _end = nullptr;}}void reserve(size_t n){if (n > capacity()){ size_t se = size();//要先保存size(),确保_finish 的位置T* tmp = new T[n];if (_start) {/*memcpy(tmp, _start, sizeof(T) * se);delete[] _start;*/ for (size_t i = 0; i < se; i++){tmp[i] = _start[i];}delete[]_start;}_start = tmp;//错误:_finish=_start+size();//size()是_finish-_start 而此时_start 已经改变_finish = _start + se;//所以要先保存原先的size()_end = _start + n;//_start 和_finish 和_end 都是 指针指向地址}}//万能初始值void resize(size_t n,const T& val=T())//val默认值怎么给---val接收T 类型的匿名对象 在这里内置类型也有(默任构造)函数 (前提是有模版){ // int i=0; int j=int(); int k=int(1); i--0 j--0 k--1if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n)//目的是为了初始化{*_finish = val;_finish++;}}}void push_back(const T& x){if (_finish == _end){size_t neew = capacity() == 0 ? 4 : 2 * capacity();reserve(neew);}*_finish = x;_finish++;//insert(end(), x);}bool empty(){return _size == 0;}void pop_back()//后删{assert(!empty());erase(--end());}size_t capacity()const{return _end - _start;}size_t size()const{return _finish - _start;}T& operator[](size_t n){assert(n < size() && n >= 0);return _start[n];}const T& operator[](size_t n)const{assert(n < size() && n >= 0);return _start[n];}iterator insert(iterator pos, const T& x)//每个位置都是指针 iterator{assert(pos >= _start&&pos<=_finish);if (_finish ==_end){size_t len = pos - _start;//记录一下pos的位置 pos之后有可能发生改变size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;//开空间的时侯,pos 有可能销毁掉,所以要换空间}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish)//数据往后覆盖掉pos{*(it - 1) = *it;it++;}_finish--;return pos;}private:iterator _start=nullptr;iterator _finish=nullptr;iterator _end=nullptr;};
总结
以上是博客的全部内容, 小编希望对各位读者朋友们有帮助