下面是完整的模拟实现,改进会在后文中提及。
#pragma once
#include
namespace chy
{
template
class vector
{
public:
typedef T* iterator;//必须共有 要不然迭代器用不了
typedef const T* const_iterator;//常量迭代器某些场景需要
//普通迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
//const_iterator常量迭代器
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//构造
vector()
:_start(nullptr);
,_finish(nullptr);
,_end_of_storage(nullptr);
{}
~vector()
{
delete[] _start;
_start=_finish=_end_of_storage=nullptr;
}
const T& operator[](size_t pos)
{
assert(poscapacity())
{
size_t sz=size();//保留size的大小
T*tmp=new T[n];
if(_start)
{ //_start不为空 把之前的数据拷贝过来
memcpy(tmp,_start,sizeof(T)*size())
delete[] _start;
}
_start=tmp;
_finsh=_start+sz;// _start+size(); 这里算size() _finish-size()抵消了
_end_of_storage=_start+n;
}
}
void push_back(const T& x)
{
//不加& vector v; v.push_back("xxxx");编不过
//加const 可以支持隐式类型转换
if(_finsh==_end_of_storage)
{
reserve(capacity()==0?4:capacity()*2)
}
*_finish=x;
_finish++;
}
//尾插可以复用insert
void push_back2()
{
insert(end(),x);
}
void pop_back()
{
assert(_finish>_start);
--_finish;
}
void insert(iterator pos,const T& x)//这里有迭代器失效问题 更新版在下面标题
{ //插入前一个赋后一个
assert(pos>=_start);
assert(pos<=_finish);
if(_finish==_end_of_storage)
{
reserve(capacity()==0?4:capacity()*2);
}
iterator end=_finish-1;//_finish的类型
while(end>=pos)
{
*(end+1)=*end;
end--;
}
*pos=x;
++_finish;
}
//存在问题 改进版见下一标题处
void erase(iterator pos)//删除从pos位置的下一个开始 后一个赋前一个
{
assert(pos>=_start);
assert(pos<_finish);
iterator begin=pos+1;
while(begin<_finish)
{
*(begin-1)=*(begin);
begin++;
}
--_finish;
//if(size()v;
v.push_back(3);
v.push_back(1);
v.push_back(9);
v.push_back(6);
v.push_back(7);
vector::iterator it=v.begin();
while(it!=v.end())
{
cout<<*it<<" ";
++it;
}
}
//常量迭代器应用场景
void Func(const vector& v)
{
vector::const_iterator it=v.begin();//这里v是const修饰的 如果不调用常量迭代器
//会出现权限放大的问题
while(it!=v.end())
{
cout<<*it<<" ";
++it;
}
}
拷贝构造
vector(size_t n,const T& val=T() )//用n个数据去构造 数据没给就去调用
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
//用T的匿名对象 去调用对应类型的默认构造
{
reserve(n);//开n个空间
for(int i=0,i
使用迭代器进行构造
现代写法(找一个打工仔)的实现需要一个有参数的构造函数,根据vector的构造函数,下面实现由迭代器实现的
这里使用模板的原因是可以适配不同容器的迭代器,只需要迭代器解引用的值在模板即可。
template
vector(InputIterator frist,InputIterator last)
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
while(frist)
{
push_back(*frist);
++frist;
}
}
int main()
{
vector v1(8);//这里调用的是上面写的构造
// 0 0 0 0 0 0 0 0
vector v2(8);//同样
// 00000000 00000000 00000000 00000000 0000000 00000000 ...
vector v3(8,1);//这里报错
return 0;
}
且报错在用迭代器构造的地方
常规写法
//v2(v1) 这里有深浅拷贝问题 最终版在下文
vector(const vector& v)
{
_start=new T[v.size()];
memcpy(_start,v._start,sizeof(T)*v.size());
_finish=_start+v.size();
_end_of_storage=_start+v.size()
}
//第二种
vector(const vector& v)
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
for(auto e:v)
{
push_back(v);
}
}
基于迭代器构造的现代写法
void swap(vector& v)
{
std::swap(_start,v._start);
std::swap(_finish,v._finish);
std::swap(_end_of_storage,v._end_of_storage);
}
vector(const vector& v)
:_start(nullptr)
,_finish(nullptr)
,_end_of_storage(nullptr)
{
vector tmp(v.begin(),v.end())
swap(tmp);
}
//v1=v2
vector& operator=(vector v)
{
swap(v);
return *this;
}
这里没带引用 v是v2的临时拷贝 用于交换
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。
可能导致迭代器失效的操作有:
1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等
2. 指定位置元素的删除操作--erase
3. 注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,尽管迭代器失效导致结果错误,程序仍然可以运行。
4. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效。
#include //算法 里面包含find
void test3()
{
vectorv;
v.push_back(2);
v.push_back(3);
v.push_back(1);
v.push_back(9);
vector::iterator it=v.begin();
while(it!=v.end())
{
cout<<*it<<" ";
++it;
}
auto p=find(v.begin(),v.end(),3);//该函数找不到返回区间的末位置
if(p!=v.end())
{
v.insert(p.30);
}
}
这里出现了野指针问题,原因是:四个数据时再插入需要扩容,而扩容是在一个新的空间并释放旧空间,pos依旧指向原来的的空间。
void insert(iterator pos,const T& x)
{
assert(pos>=_start);
assert(pos<=_finish);
if(_finish==_end_of_storage)
{
size_t len=pos-_start;
reserve(capacity()==0?4:capacity()*2);
//更新pos
pos=_start+len;
}
iterator end=_finish-1;//_finish的类型
while(end>=pos)
{
*(end+1)=*end;
end--;
}
*pos=x;
++_finish;
}
要求删除所有偶数
void test4()
{
vectorv;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
auto it=v.begin();
while(it!=v.end())
{
if(*it%2==0)
{
v.erase(it);
}
++it;
}
原因是出现偶数时删除数据只是数据的移动,而指针依然指向原位置,此时再++会跳过数据,甚至因为earse中_finish--而导致it指针错过_finish而越界
void test4()
{
vectorv;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
auto it=v.begin();
while(it!=v.end())
{
if(*it%2==0)
{
v.erase(it);
}
else
{
++it;
}
}
}
//stl规定 earse返回删除位置下一个位置迭代器
iterator erase(iterator pos)
{
assert(pos>=_start);
assert(pos<_finish);
iterator begin=pos+1;
while(begin<_finish)
{
*(begin-1)=*(begin);
begin++;
}
--_finish;
return pos;
}
如果使用下面方法模拟实现中的拷贝构造
//拷贝构造 v2(v1)
vector(const vector& v)
{
_start=new T[v.size()];
memcpy(_start,v._start,sizeof(T)*v.size());
_finish=_start+v.size();
_end_of_storage=_start+v.size();
}
当使用类型为vector
即T对象是自定义类型时。
仅有vector
如果使用的是老式方法,即直接调用拷贝构造,那么需要改变的是拷贝构造。
这里使用赋值产生的中间变量以解决上述问题。
vector(const vector& v)
{
_start=new T[v.size()];
//memcpy(_start,v._start,sizeof(T)*v.size());
for(size_t i=0;i
如果使用现代写法,即先利用迭代器实现构造,再进行交换实现的拷贝构造方法。由于迭代器构造使用了push_back(),在该函数中涉及reserve()函数,扩容是涉及深浅拷贝的。所以需要改变的就是reserve()函数。
与上述问题相同,有memcpy的地方就是浅拷贝。
解决方案
同样采用赋值方式。
void reserve(size_t n)
{
if(n>_capacity)
{
size_t sz=size();
T*tmp=new T[n];
if(_start)
{
for(size_t i=0;i