标题:C++中vector容器迭代器的运算和失效问题详解

正文:
在C++中,vector是一个非常常用的容器,它可以动态地存储数据,并且支持随机访问。在使用vector容器时,我们经常需要使用迭代器来遍历容器中的元素。本文将详细介绍迭代器的运算符以及迭代器失效的问题,并给出相应的示例代码。

一、迭代器运算符


1. *iter:返回迭代器iter所指向元素的引用。

必须保证这个迭代器指向的是有效的容器元素,不能指向end();需要注意的是,迭代器不能指向end(),因为end()是末端元素的后一个元素,是不存在的元素,所以不能指向end()。

2. ++iter; iter++:让迭代器指向容器中的下一个元素。当迭代器指向end()时,不能再进行++操作。

3. --iter; iter--:让迭代器指向容器的上一个元素。当迭代器指向begin()时,不能再进行--操作。

4. iter1 == iter2; iter1 != iter2:判断两个迭代器是否相等或不相等。如果两个迭代器指向的是同一个元素,则相等,否则不相等。

5. 引用结构中的成员:如果容器中存储的是结构体对象,可以通过迭代器访问结构体的成员。例如,可以使用(*iter).num或iter->num来访问结构体中的num成员。

    struct student
	{
		int num;
	};
	vector<student>sv;
	student mystu;
	mystu.num = 1010;
	sv.push_back(mystu);//把对象mystu赋值到sv容器中
	vector<student>::iterator iter;
	iter = sv.begin();//指向第一个元素
	cout << (*iter).num << endl;
	cout << iter->num << endl;

二、迭代器失效问题

在使用vector容器时,插入或删除元素可能会导致迭代器失效。迭代器失效意味着迭代器指向的元素已经被删除或移动,再使用该迭代器可能会导致未定义的行为。

1. 插入元素导致迭代器失效:当在容器中插入元素时,插入位置之后的所有迭代器都会失效。因此,在插入元素后,要重新获取迭代器或者及时跳出循环。

	*vector<int>i1 = { 1,2,3,4,5 };
	for (auto i : i1) {
		i1.push_back(888)//报错
		cout << i << endl;
	  }
	  for (auto beg = i1.begin(), end = i1.end(); beg != end; ++beg) {
		  i1.push_back(888);迭代器失效
		cout << *beg << endl;
	  }
	//总结:在操作迭代器的操作过程中(使用了迭代器这种循环体),
    //  千万不要改变vector容器的容量
	//  也就是不要增加或删除vector中的元素
	//  往容器中增加或者从容器中删除元素,
    //  这些操作可能会使指向容器元素的指针、引用、迭代器失效。
	//  失效就表示不能再代表任何容器的元素。
    //  一旦失效的东西,就等于犯了严重的程序错误,很多情况下,程序会直接崩溃

2. 删除元素导致迭代器失效:当删除元素时,被删除元素之后的所有迭代器都会失效。因此,在删除元素后,要重新获取迭代器或者使用erase函数返回的下一个元素位置。

三、为了避免迭代器失效,可以采用以下方法:

1. 使用erase函数删除元素时,将erase函数返回的下一个元素位置赋值给迭代器。

2. 使用while循环和empty()函数结合,判断容器是否为空,然后使用erase函数删除元素,直到容器为空。

下面是一个示例代码,演示了如何删除vector容器中的元素并释放内存

 vector容器最简单的删除元素方法:
    vector<int>i = { 1,2,3,4,5 };
    while (!i.empty()) {
	    auto iter = i.begin();//因为不为 空 ,所有返回begin()是没问题
	    i.erase(iter);//删除该位置上的元素。
	}

在这个示例中,我们使用while循环和empty()函数来判断容器是否为空,然后使用erase函数删除元素。注意,在每次删除元素后,我们重新获取了begin()迭代器,以确保迭代器的有效性。通过以上方法,我们可以安全地删除vector容器中的元素,并释放相应的内存。

灾难程序演示1
      vector<int>i1 = { 1,2,3,4,5 };
      auto beg = i1.begin();
      auto end = i1.end();
      while (beg != end) {
    	cout << *beg << endl;
    	//假如我们想往begin这个位置插入新值,可以用insert
    	i1.insert(beg, 80);
//插入新值,第一个参数为插入位置,第二个参数为插入的元素
//咱们这一插入,肯定会导致迭代器失效,比如begin,end失效
//具体的哪个迭代器失效,取决于这个容器vector内部的工作原理,
//我们可以查资料详细研究
//现在我们不太明确哪个迭代器失效了,最明志的做法,就是我们只要以插入数据,
//插入完毕就break跳出循环体
   	    break;//最明智的防止迭代器的方法,否则迭代器会失效。
   	    ++beg;//不要忘记,并且要放循环末尾
      }
    beg = i1.begin();
    end = i1.end();
    while (beg != end) {
   	cout << *beg << endl;
  	++beg;//不要忘记,并且要放在循环末尾。
    }
灾难演示程序2
    *vector<int>i = { 1,2,3,4,5 };
    for (auto i1 = i.begin(); i1 != i.end(); ++i1) {
	    i.erase(i1);
      //erase函数,移除i1位置上的元素,返回下一个元素位置。
    }
	下面改造:元素释放
	vector<int>::iterator iter = i.begin();
	vector<int>::iterator iterend = i.end();
	while (iter != i.end()) {
	   iter = i.erase(iter);
	}

四、const_iterator迭代器

迭代器是用来遍历容器中的元素的工具,而const_iterator是一种特殊的迭代器表示指向的元素值不能被改变。这意味着通过const_iterator迭代器只能读取容器中的元素,而不能修改它们

下面是一个示例代码,演示了使用const_iterator迭代器遍历vector容器中的元素:

vector<int> i1 = { 100,200,300 };
vector<int>::const_iterator citer;
for (citer = i1.begin(); citer != i1.end(); citer++) {
    *citer = 1; // 不能修改元素的值
    cout << *citer << endl; // 可以正常读取元素的值
}

在这个示例中,我们使用const_iterator迭代器遍历了vector容器中的元素注意,我们不能通过const_iterator迭代器修改元素的值,但是可以正常地读取元素的值。

另外,C++11引入了两个新函数cbegin()和cend(),它们分别返回一个常量迭代器,指向容器的第一个元素和最后一个元素的后一个位置。这意味着通过cbegin()和cend()返回的迭代器,我们只能读取容器中的元素,而不能修改它们。下面是一个示例代码:

vector<int> i = { 1000,2000,3000 };
for (auto iter = i.cbegin(); iter != i.cend(); ++iter) {
    //*iter = 1; // 报错,不能给常量赋值
    cout << *iter << endl; // 可以正常读取元素的值
}

在这个示例中,我们使用cbegin()和cend()函数返回的常量迭代器遍历了vector容器中的元素。注意,我们不能通过这些常量迭代器修改元素的值,只能读取元素的值。

总结:
const_iterator是一种特殊的迭代器,表示指向的元素值不能被改变。通过const_iterator迭代器,我们只能读取容器中的元素,而不能修改它们。C++11引入的cbegin()和cend()函数返回的是常量迭代器,也只能用于读取容器中的元素。在实际编程中,根据需要选择合适的迭代器类型,以保证对容器的操作符合需求。

五、实践范例演示

用迭代器遍历一下string类型数据
    string str{ "I Love China!" };
    for (auto s = str.begin(); s != str.end(); s++) {
 	    *s = toupper(*s);//转成大写
    }
    cout << str << endl;
//vector容器常用操作与内容释放:
  //实践程序:
  //ServerName = 1区     表示服务器名称
  //ServerID = 100000    表示服务器ID
struct conf
{
	char itemname[40];
	char itemcontent[100];
};
char *getinfo(vector<conf*>&conflist,const char *pitem)
{
	for (auto pos = conflist.begin(); pos != conflist.end(); ++pos) {
		if (stricmp((*pos)->itemname, pitem) == 0) {
			return (*pos)->itemcontent;
		}
	}
	return nullptr;
}
int main(){

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), "100000");

vector<conf*>conflist;
conflist.push_back(pconf1);//conflist[0]
conflist.push_back(pconf2);//conflist[1]


char* p_tmp = getinfo(conflist, "ServerName");
if (p_tmp != nullptr) {
	cout << p_tmp << endl;
}

//我们要释放内存,自己new的就要自己释放,否则会造成内存泄漏
std::vector<conf*>::iterator pos;
for (pos = conflist.begin(); pos != conflist.end(); ++pos) {
	delete (*pos);//*pos才是那个指针
}
conflist.clear();//这个要不要都行,系统会自动释放
	return 0;
}

总结:
本文详细介绍了C++中vector容器迭代器的运算符和迭代器失效问题,并给出了相应的示例代码。在使用vector容器时,我们需要注意迭代器的运算和失效问题,以避免未定义的行为。同时,通过合理的操作和使用erase函数返回的下一个元素位置,我们可以安全地删除vector容器中的元素,并释放内存。

希望本文能够帮助读者理解迭代器的运算和失效问题,并在实际编程中正确地使用vector容器。如有疑问或建议,欢迎留言讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值