正文:
在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容器。如有疑问或建议,欢迎留言讨论。