c语言入门到c++使用高手:深入学习C++之基本语言(九)

第二章 基本语言

第九节 迭代器精彩演绎,失效分析及弥补、实战

1. 迭代器简介

  • 迭代器是一种遍历容器内元素的数据类型,这种数据类型有点像指针,我们理解为迭代器用来指向容器中的某个元素

  • string, vector,[], 很少用[],更常用的访问方式就是用迭代器(更通用)

  • 通过迭代器,我们可以读容器中的元素值,读string中的每个字符,还可以修改某个迭代器所指向的元素值

  • ++/–

  • list, map,尽量学会用迭代器来访问容器中的元素

2. 容器的迭代器类型

 vector<int> vint{100, 200, 233};
    // 把整个vector<int>::iterator 理解成一个类型,这种类型专门应用于迭代器
    // 当我们用这个类型定义一个变量的时候,这个变量就是迭代器,这里的iter这个变量就是个迭代器
    vector<int>::iterator iter;//定义迭代器,也必须是vector<int>
    

3. 迭代器begin()/end()操作,反向迭代器rbegin()/rend()操作

  • begin()/end()用来返回迭代类型, rbegin()/rend()也是用来返回迭代类型

  • begin():如果容器中有元素,则begin()返回的迭代器,指向的是容器的第一个元素

  • end():返回一个迭代器类型

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> vint{100, 200, 233};
    vector<int>::iterator iter;//定义迭代器


//    iter指向了vint[0]
    iter = vint.begin();
//    end返回的迭代器指向的并不是末端元素,而是末端元素后边
//    可以理解成end()指向的是一个不存在的元素
    iter = vint.end();
    return 0;
}
  • 如果容器为空的话,begin()和end()返回的迭代器相同
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> vint{};
    vector<int>::iterator iterb, itere;//定义迭代器
    iterb = vint.begin();
    itere = vint.end();
    if(iterb==itere){
        cout << "容器为空!" << endl;
    }
    return 0;
}
  • 传统写法
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> vint{1, 3, 393};
    for (vector<int>::iterator iter = vint.begin(); iter != vint.end(); iter++) {
        cout << *iter << endl;
    }
  
    return 0;
}
  • 反向迭代器: 想从后往前遍历一个容器,那么使用反向迭代器就比较方便

  • rbegin():返回反向迭代器,返回反向迭代器的第一个元素

  • rend(): 返回一个反向迭代器,返回反向迭代器的最后元素的下一个位置

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> vint{1, 3, 393};
    for (vector<int>::reverse_iterator riter = vint.rbegin(); riter != vint.rend(); ++riter) {
        cout << *riter << endl;
    }
    return 0;
}

4. 迭代器运算符

  • *iter:返回迭代器iter所指向元素的引用,必须要保证这个迭代器指向的是有效的容器元素,
    不能指向end(),因为end()是末端元素的后边,即是一个不可控的位置

  • ++iter,iter++: 让迭代器指向容器中的下一个元素,已经指向end()的时候,不能再进行自加

  • –iter, iter–:让迭代器指向容器中的上一个元素,已经指向begin()的时候,不能再进行自减

  • iter1 == iter2, iter1 != iter2,判断两个迭代器是否相等,如果两个迭代器指向的是同一个元素,就相等,否则就不相等

  • 容器中是结构体,如何引用结构中的成员

#include <iostream>
#include <string>
#include <vector>

using namespace std;

struct student {
    int num;
};

int main() {
    vector<student> sv;
    student stu;
    stu.num = 100;
    sv.push_back(stu);

    vector<student>::iterator iter;
    iter = sv.begin();
    cout << (*iter).num << endl;
    cout << iter->num << endl;
    return 0;
}

5. const_iterator迭代器

  • const: 表示常量

  • const_iterator迭代器,表示迭代器指向的元素值不能改变,指向的位置可以改变
    所以这个迭代器只能从容器中读元素,不能改写容器中的元素,更像一个常量指针

#include <iostream>
#include <string>
#include <vector>

using namespace std;


int main() {
    vector<int> iv{100, 200, 300};
    vector<int>::const_iterator citer;
    for(citer = iv.begin(); citer != iv.end(); citer++){
        // 会报错
        //*citer = 1;
        cout << *citer <<endl;//可以正常读
    }
    return 0;
}
5.1 cbegin()和cend()操作
  • c++ 引入的两个新函数cbegin(),cend()跟begin()和end()类似,cbegin()和cend返回的都是常量迭代器
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    for( auto iter = iv.cbegin(); iter != iv.cend(); iter++){
        // 会报错,因为返回的是常量迭代器
        //*iter = 1;
        cout << *iter <<endl;//可以正常读
    }
    return 0;
}

6. 迭代器失效

  • 在操作迭代器的过程中,不要改变容器的容量,也就是不要增加或者删除vector容器中的元素

  • 在容器中增加或者从容器中删除元素,这些操作可能会使指向容器元素的指针,引用,迭代器失效,
    失效就表示不能再代表容器中的元素,一旦使用失效的对象,程序就很容易崩溃

#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    for (auto beg = iv.begin(); beg != iv.end(); beg++) {
        // 导致程序崩溃
        iv.push_back(1);
        cout << *beg << endl;
    }
    return 0;
}
  • 解决方案,如果一定要插入一个数据,加入break,然后重新开启一个循环
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    for (auto beg = iv.begin(); beg != iv.end(); beg++) {
        // 导致程序崩溃
        iv.push_back(1);
        break;
    }
    for (auto beg = iv.begin(); beg != iv.end(); beg++) {
        cout << *beg <<endl;
    }
    return 0;
}
6.1 灾难程序演示1
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    auto beg = iv.begin();
    auto end = iv.end();
    while (beg != end) {
        cout << *beg << endl;
        //插入新值
        //加入想往begin这个位置插入新值第一个参数为插入的位置,第二个参数为插入的元素
        //会导致迭代器失效,具体哪部分失效,依赖于容器的内部实现,最好的做法是break出来
        iv.insert(beg,88);
        break;//最明智的防止迭代器失效的方法,否则程序很可能崩溃
        ++beg;
    }

    auto beg1 = iv.begin();
    auto end1 = iv.end();
    while (beg1 != end1) {
        cout << *beg1 << endl;
        ++beg1;
    }


    return 0;
}
  • 保证迭代器不失效的代码
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    auto beg = iv.begin();
    // 每次更新end(),防止end迭代器失效
    int icount = 0;
    while (beg != iv.end()) {
        // insert的返回结果用beg接收
        beg = iv.insert(beg, icount + 10);
        icount++;
        if (icount > 5) {
            break;
        }
        ++beg;
    }

    auto beg1 = iv.begin();
    auto end1 = iv.end();
    while (beg1 != end1) {
        cout << *beg1 << endl;
        ++beg1;
    }
    
    return 0;
}
6.2 灾难程序演示2
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    //...
    for (auto iter = iv.begin(); iter != iv.end(); ++iter) {
        // earse函数,移除iter位置上的元素,返回下一个元素位置
        iv.erase(iter);//迭代器失效,导致程序崩溃
    }
    return 0;
}
  • 修改代码
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    //...
    vector<int>::iterator iter = iv.begin();
    while(iter != iv.end()){
        iter = iv.erase(iter);
    }
    return 0;
}
  • 针对vector,一个一个删除元素的方法
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    vector<int> iv{100, 200, 300};
    while(!iv.empty()){
        auto beg = iv.begin();//因为不为空,所以返回begin()是没问题的
        iv.erase(beg);//删除该位值元素
    }
    return 0;
}

7. 范例演示

7.1 用迭代器遍历一下string类型数据
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int main() {
    string str{"I am Felaim!"};
    for (auto iter = str.begin(); iter != str.end(); ++iter) {
        *iter = toupper(*iter);
    }
    cout << str << endl;
    return 0;
}
7.2 vector容器常用操作与内存释放

实战程序

ServerName = 1//表示服务器名称
ServerID =88 // 表示服务器ID
  • 完整实例
#include <iostream>
#include <string>
#include <vector>
#include <cstring>

using namespace std;
struct conf {
    char itemname[40];
    char itemcontent[100];
};


char *getinfo(vector<conf *> &conflist, char *pitem) {
    for (auto iter = conflist.begin(); iter != conflist.end(); ++iter) {
        if (strcmp((*iter)->itemname, pitem) == 0) {
            return (*iter)->itemcontent;
        }
    }
}

int main() {

    // new出来的内存要释放!!!
    conf *pconf1 = new conf;
    strcpy(pconf1->itemname, "ServerName");
    strcpy(pconf1->itemcontent, "1区");

    conf *pconf2 = new conf;
    strcpy(pconf2->itemname, "ServerID");
    strcpy(pconf2->itemcontent, "1000");

    vector<conf *> conflist;
    conflist.push_back(pconf1);
    conflist.push_back(pconf2);

//    for (auto iter = conflist.begin(); iter != conflist.end(); ++iter) {
//        cout << (*iter)->itemname << endl;
//        cout << (*iter)->itemcontent << endl;
//    }

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

    // 需要释放内存
    vector<conf *>::iterator pos;
    for (pos = conflist.begin(); pos != conflist.end(); ++pos) {
        //*pos才是对应的指针,并没有破坏迭代器,释放的是迭代器指向的额外内存
        delete ((*pos));
    }
    conflist.clear();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值