c++知识细节-迭代器及失效分析/实战

c++知识细节-迭代器及失效分析/实战

简介

迭代器是一种遍历容器内元素的数据类型,有点像指向容器中各个元素的指针.在c++中,很少用[]下标的形式来访问容器,因为可能某种容器不能使用[]来访问,而迭代器的方式是所有容器都支持的,更通用.

容器的迭代器类型

vertor<int> iv{100,200,300};
vector<int>::iterator iter;	//定义迭代器时,也必须是vector<int>下定义的iterator成员.

可以把vector::iterator整个理解成一个数据类型,且与容器类型紧密相关.

迭代器操作

  • begin()/end(): 用来返回容器的迭代器类型.
iter = iv.begin();//如果容器中有元素,则begin返回一个迭代器,指向容器中的第一个元素
iter.iv.end();	//end返回的并不是最后一个元素,而是末端元素的后边.,可以理解成一个不存在的元素.

如果容器为空,则begin()和end()返回的是同一个迭代器,都是尾后迭代器.

  • 具体使用案例
vector<int> iv = {100,200,300};
for (vector<int>::iterator iter = iv.begin(); iter!= iv.end(); iter++)
{
	cout << *iter;	//100,200,300
}
  • rbegin()/rend(): 反向迭代器.应用于想从后往前遍历的场景.r代表reverse.注意其对应的类型为vector::reverse_iterator.
for (vector<int>::reverse_iterator riter = iv.rbegin(); riter!= iv.rend(); riter++)
{
	cout << *riter;	//300,200,100
}

迭代器运算符

  • *iter: 返回迭代器iter所指向元素的引用.必须保证这个迭代器指向的是有效的容器元素;不能指向end(),因为end()指向了末端元素的后边,是一个不存在的元素.
iter = iv.begin();
cout << *iter;
  • iter++;++iter: 让迭代器指向下一个元素,已经指向end()后就不能再++了.
  • iter–;--iter: 让迭代器指向容器中上一个元素,已经指向begin()后就不能再–了.

const_iterator迭代器

常量迭代器.此迭代器指向的元素值不能改变,迭代器的指向可以改变.即只能从容器中读元素,而不能改写容器中的元素值,感觉像是一个常量指针.

vector<int>::const_iterator citer = iv.begin();

Tips:如果容器对象为常量,则遍历时必须用常量迭代器.

  • cbegin()/cend(): c++11新引入的两个新函数,这两个函数返回的都是常量迭代器,此时可以用auto类型来接.

迭代器失效问题

范围for等价与使用迭代器来遍历容器内元素.

for (auto ivitem: iv)
{
	iv.push_back(666);
	cout << ivitem;
}

等价于

for (auto iter = iv.begin(); beg != iv.end(); iter++) 
{
	iv.push_back(666);
	cout << *iter;
}

在操作迭代器的过程中,千万不要改变容器的容量,也就是不要增删容器中的元素.这些操作可能回事指向容器元素的指针,引用,迭代器失效,一旦失效,就不能再代表任何容器中的元素,此时若继续使用这些失效的指针,迭代器等,就会发生程序错误,甚至导致程序崩溃.

Tips:如果真的要在循环中插入数据,一定要在插入后即时更新迭代器.比如退出循环重新开一个新循环.或者是使用insert()后返回新的迭代器来覆盖旧的迭代器.例子如下:

beg = iv.begin();
int icount = 0;
while(beg != iv.end())
{
	beg = iv.insert(beg,icount + 80);
	icount++;
	if (icount > 10)
		break;
	++beg;
}

此例子中,每次insert元素后都对beg迭代器进行了更新,且每进入下一次进入循环时也对end()迭代器进行了更新.循环退出的条件是用icount来控制的.虽然这样写不会有问题,单还是不推荐这种写法.

  • vector容器的清空方法

erase(iter): 移除iter位置上的元素,返回下一个元素的位置

如果vector内元素为普通类型,可以直接用iv.clear()方法来实现清空,但是如果vector内存放的是指针元素,此时需要单独去清除每个指针指向的内存.如果直接循环erase()会发生失效问题:

for (auto iter = iv.begin(); iter != iv.end(); iter++)
{
	iv.erase(iter);
}

可用类型下面代码的方法来避免迭代器失效问题.

while(!iv.empty())
{
	auto iter = iv.begin();	//每次都更新begin()
	iv.erase(iter);
}

范例演示

  • 用迭代器遍历string类型数据
string str("I Love China");
for (auto iter = str.begin(); iter != str.end(); iter++)
{
	*iter = toupper(*iter);
}
cout << str;
  • vector常用操作与内存释放
struct conf
{
	char itemname[40];
	char itemcontent[100];
};
conf * pconf1 = new conf;
strcpy_s(pconf1->itemname, sizeof(pconf1->itemname),"ServerName");
strcpy_s(pconf1->itemcontent, sizeof(pconf1->itemcontent),"1区");
conf * pconf2 = new conf;
strcpy_s(pconf2->itemname, sizeof(pconf2->itemname),"ServerId");
strcpy_s(pconf2->itemcontent, sizeof(pconf2->itemcontent),"111111");
vector<conf *> conflist;
conflist.push_back(pconf1);	//[0]
conflist.push_back(pconf2);	//[1]
char * p_tmp = getinfo(conflist, "ServerName");
//用完之后我们要释放内存
vector<conf*>::iterator pos;
for (pos = conflist.begin();pos != conflist.end();pos ++)
{
	delete (*pos);	//*pos才是指向内容的指针
}
/********/

char * getinfo(vector<conf *> &conflist, const char *pitem)
{
	for (auto pos = conflist.begin(); pos != conflist.end();++pos)
	{
		if (_strcmp((*pos)->itemname,pitem) == 0)
		{
			return (*pos)->itemcontent;
		}
	}
	return nullptr;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值