目录
一.迭代器介绍:
在学习C++时候,我们已经知道可以用下标运算符访问string和vector对象的字符或者vector对象的元素,但是还有另一种机制可以帮助我们同样完成访问,这就是迭代器。类似于指针类型,迭代器也可以间接的访问对象,使用迭代器访问对象中的元素,也就是迭代器从一个元素移动到另一个元素中去。迭代器分为有效和无效,和指针一样,有效的迭代器指向具体的某个元素或者迭代器指向容器中最后一个元素的下一个位置,其余为无效,也就是指向的是无意义位置或者越界访问,类似于野指针。迭代器为iterator。
二.使用迭代器:
(一).迭代器运算符:
迭代器运算符==和!=用来比较两个迭代器是否相等还是不相等,如果两个迭代器指向的元素相同或者都是一个同一个容器的尾后迭代器,则它们相等,反之不相等。
1.operator*()函数会返回解引用的元素,operator*()返回的是元素的引用,而不是副本或者形参,改变返回的值也就改变了vector相应元素的值,但是注意:operator*()修改值适用于非常量元素。
std::vector<int> v1={1,2,3};
vector<int>::iterator it=v1.begin();
*it;//解引用
2.operator++()函数 可以对迭代器对象自行增加,主要分为两种:前置++、后置++。
-
前置递增操作符 ++ : 返回一个引用到修改后的迭代器本身 , 允许你在一个语句中递增迭代器并使用它 ;
-
后置递增操作符 ++ : 返回一个新的迭代器 , 该迭代器指向下一个元素 , 原来的迭代器保持不变 ; 这个操作符重载了 int 参数,以避免与前置递增操作符的优先级混淆
iterator&operator++();
iterator &operator++(int);//后置++
3. operator=(),主要用来赋值,传入的元素会覆盖原来的元素,并且可以同时修改原来容器的size。
#include <vector>
#include <iostream>
int main(){
std::vector<int> v1(5,0);//表示有五个相同的元素并且初始化为0
std::vector<int> v2(3,0);
v1=v2;
cout<<"size of v1:"<<int(v1.size())
cout<<"size of v2"<<int(v2.size())
最终结果是size of v1=3。
4.迭代器遍历访问:
我们在访问 数组时候会遍历数组下标,通过迭代器遍历可以间接的获得元素,使用begin和end函数,类似于两个指针,头指针和尾指针。
int main(){
std::vector<int> v1={1,2,3,4,5};
std::vector<int>::iterator it=v1.begin();
for(auto e:v1){
cout<<"e"<<""
}
cout<<endl;
#include <iostream>
#include <vector>
using namespace std
int main()
{
std::vector<int> v1={1,2,3,4,5};
std::vector<int>::iterator it=v1.begin();
for(auto=v1.begin();it!=v1.end();++it)
{
cout<<*it<<""
}
cout<<<endl
(二).迭代器类型:
template<class T>
typedef T* iterator; // 迭代器某种意义上就是指针
typedef const T* const_iterator;
迭代器类型主要有两个:第一种是可读可写、第二种是只可读不可修改。typedef T*iterator就是一个可读可修改的迭代器,typedef const T*const_iterator只可读不能修改指针指向的元素值。
(三).迭代器的运算:
三.迭代器失效:
(一).什么是迭代器失效?
1.迭代器的作用:主要让算法不用关心底层的数据结构,本质上是对指针的封装。
2.迭代器失效:迭代器的本质其实就是类似于指针,所以迭代器失效就是指针指向的空间被销毁或者指针越界访问,最终造成程序崩溃。可以理解如下:
- [1]迭代器的本质就是指针,迭代器失效就是指针失效。
- [2]指针失效:指针指向的空间是非法的。
- [3]指针指向非法空间:指向了被释放的空间 或者 越界访问 。
(二).哪些操作会导致迭代器失效?
1.所有引起扩容的操作都会可能导致迭代器失效,如:reserve、push_back等,这其实本质是野指针,最终导致迭代器失效。
2.所有增加或者删除操作都会有可能导致迭代器失效,如:insert、erase等,其本质是指向的空间无意义。
(三).如何避免迭代器失效:
在本次讲解中,主要用vector中的erase和insert来讲。
1.迭代器指针指向野指针:
void insert(iterator pos,const T&x){
assert(_start);
if(_finish==_end_of_storage)//判断空间是否已经满了,如果满了进行扩容
{
size_t newcapacity=capacity()==0?4:capacity()*2;
reserve(newcapacity);
}
//扩容成功
while(end>=pos){
*(end+1)=*end;
--end;
}
*pos=x;
--_finish;
return pos;
}
为什么会出现随机值?这是因为在进行扩容的时候,_start和_finish的位置发生了变化,旧空间最终被释放,但是pos的指向空间没有更新,所以产生随机值,导致访问了野指针。
解决方案:在进行扩容前先进行计算出pos的长度,然后在扩容后再指出pos在新空间的位置。
void insert(iterator pos,const T&x){
assert(_start);
if(_finish==_end_of_storage)//判断空间是否已经满了,如果满了进行扩容
{
size_t len=pos-_start;
size_t newcapacity=capacity()==0?4:capacity()*2;
reserve(newcapacity);
pos=_start+len;
}
//扩容成功
while(end>=pos){
*(end+1)=*end;
--end;
}
*pos=x;
--_finish;
return pos;
}
2.指向的空间无意义:
例子:我们要在一组数据中的偶数前面插入2
void test2()
{
xas_vector::vector<int> v1;
v1.Push_back(1);
v1.Push_back(2);
v1.Push_back(3);
v1.Push_back(4);
v1.Push_back(5);
v1.Push_back(6);
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
xas_vector::vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
if (*it % 2 == 0)
{
v1.insert(it, 20);
it++;
}
it++;
}
for (auto ch : v1)
{
cout << ch << " ";
}
cout << endl;
}
erase删除数据:
给 erase函数 加上返回值即可解决,返回指向新插入元素的位置。