美文网首页互联网@时事传播C/C++程序设计专题想法
C++系列 --- 迭代器,失效分析及弥补、实战

C++系列 --- 迭代器,失效分析及弥补、实战

作者: 307656af5a04 | 来源:发表于2019-04-24 06:11 被阅读11次

    一、迭代器简介

    迭代器是一种遍历容器内元素的数据类型。这种数据类型感觉有点像指针。

    string,vector,[],很少用[],更通用的方式就是用迭代器。

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

    迭代器支持++,--自增,自减操作,相关list/map容器

    二、容器的迭代器类型

    vector<int> iv(100,200,300);
    // 定义迭代器,也必须是vector<int>
    vector<int>::iterator iter; 
    

    三、迭代器的begin()/end(),反向迭代器rbegin()/rend()操作

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

    (1)begin()返回一个迭代器类型

    // 如果容器中有元素,则begin返回的迭代器,
    // 指向容器中的第一个元素。 即iter指向了iv[0]
    iter = iv.begin();
    

    (2)end():返回迭代器类型

     // end返回的迭代器指向的
    // 并不是末端元素,而是末端元素的后边
    iter = iv.end();
    

    (3)如果一个容器为空,则begin()/end()返回的迭代器相同

    vector<int> iv2;
    vector<int>::iterator iterbegin = iv2.begin();
    vector<int>::iterator iterend = iv2.end();
    if(iterbegin == iterend)
    {
        cout << “容器iv2为空” << endl;
    }
    

    (4)传统迭代器的写法

    vector<int> iv(100,200,300);
    for(vector<int>::iterator iter = iv.begin();
    iter != iv.end(); iter++)
    {
        // 依次打印100,200,300
        cout << *iter << endl; 
    }
    

    (5)反向迭代器

    反向迭代器:从后往前遍历容器中的元素

    反向迭代器(逆向迭代器),用的rbegin(),rend()

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

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

    vector<int> iv(100,200,300);
    for(vector<int>::reverse_iterator riter =
     iv.rbegin(); riter != iv.rend(); riter++)
    {
        // 依次打印300,200,100
        cout << *riter << endl; 
    }
    

    四、迭代器运算符

    (1)*iter:返回迭代器iter所指向元素的引用。必须保证这个迭代器指向的是有效的容器元素,不能指向end(),因为end()是末端后面的元素,即end()是指向一个不存在的元素。

    vector<int> iv(100,200,300);
    // 存在问题
    //vector<int>::iterator iter = iv.end(); 
    vector<int>::iterator iter = iv.begin();
    cout << *iter << endl;
    

    (2)++iter,iter++:让迭代器指向容器的下一个元素;已经指向end()时候,不能再自加,自减。

    //vector<int>::iterator iter = iv.end(); // 存在问题
    vector<int>::iterator iter = iv.begin();
    iter++;
    cout << *iter << endl;
    

    (3)--iter,iter--:让迭代器指向容器中的上一个元素。指向开头元素,不能再--

    //vector<int>::iterator iter = iv.begin(); // 存在问题
    vector<int>::iterator iter = iv.end();
    iter--;
    cout << *iter << endl;
    

    (4)iter1 == iter2; iter1 != iter2 判断两个迭代器是否相等。

    如果两个迭代器指向的是同一个元素,就相等,否则就不等。

    (5)如何引用结构中的成员

    struct student
    {
    int num;
    };
    
    vector<student> sv;
    student mystu;
    mystu.num = 100;
     // 把对象mystu赋值到了sv容器中。
    sv.push_back(mystu);
    
    // 确保迭代器指向有效的对象
    vector<student>::iterator iter; 
    iter = sv.begin(); // 指向第一个元素
    cout << (*iter).num << endl; 
    cout << iter->num << endl;
    

    五、const_iterator迭代器

    const_iterator迭代器,表示值不能改变的意思,这里的值不能改变表示这个迭代器指向的元素的值不能改变,而不是表示这个迭代器本身不能改变,即迭代器本身是可以不断指向下一个元素;只能从容器中读元素,不能通过这个迭代器改写容器中的元素,感觉起来更像常量指针。

    const vector<int> iv = {100,200,300};
    vector<int>::const_iterator iter;
    for(iter = iv.begin(); 
        iter != iv.end(); iter++)
    {
        //*iter = 4; // 出错,不可修改
        cout << *iter << endl;
    }
    

    (1)cbegin()/cend()操作

    C++11引入的两个新函数,跟begin,end类似;cbegin,cend,返回的都是常量迭代器

    for(auto iter = iv.cbegin();
       iter != iv.cend(); iter++)
    {
        // 报错,不能给常量赋值,
        // 这说明cbegin返回的是常量迭代器。
        //*iter = 4; 
        cout << *iter << endl;
    }
    

    六、迭代器失效

    vector<int> vecvalue{1,2,3,4,5};
    
    for(auto vecitem : vecvalue)
    {
        // 报错,显示结果混乱
        //vecvalue.push_back(444); 
        cout << vecitem << endl;
    }
    
    for(auto beg = vecvalue.begin(); 
        beg != vecvalue.end(); beg++)
    {
         // 报错,崩溃
        //vecvalue.push_back(444);
        cout << *beg << endl;
    }
    

    在操作迭代器的过程中,使用迭代器这种循环体,千万不要改变vector容器的容量。即不要增加或删除vector容器中的容量。

    向容器中增加元素或删除元素,这些操作可能会使指向容器元素的指针,引用,迭代器失效,失效表示不能再代表任何容器中的元素。一旦使用失效的东西,就等于犯了严重的程序错误,很多情况下,程序会直接崩溃。

    for(auto beg = vecvalue.begin(); 
        beg != vecvalue.end(); beg++)
    {
        vecvalue.push_back(444);
        break; // 插入新元素直接跳出
    }
    
    for(auto beg = vecvalue.begin(); 
        beg != vecvalue.end(); beg++)
    {
        cout << *beg <<endl;
    }
    

    (1)灾难程序1

    vector<int> vecvalue{1,2,3,4,5};
    auto beg = vecvalue.begin();
    auto end = vecvalue.end();
    
    while(beg != end)
    {
        cout << *beg << endl;
        // 加入想往begin这个位置插入新值,
        // 可以用insert
        // 插入新值,第一个参数为插入位置,
        // 第二个参数为插入值。
        vecvalue.insert(beg,80); 
        // 这个值的插入,会使迭代器失效。比如begin,end失效
        // 具体哪个迭代器失效,取决于vector内部的实现原理。
        // 最明智的做法就是立即break跳出循环,否则程序会崩溃。
        break;
       // beg++; // 不要忘记,并且放在循环末尾
    }
    
    beg = vecvalue.begin();
    end = vecvalue.end();
    
    while(beg != end)
    {
        cout << *beg << endl;
        beg++; 
    }
    
    // 迭代器插入防止迭代器失效
    vector<int> vecvalue{1,2,3,4,5};
    auto beg = vecvalue.begin();
    auto end = vecvalue.end();
    int icount = 0;
    
    // 每次更新end防止end失效
    while(beg != vecvalue.end())
    {
        beg = vecvalue.insert(beg,icount+80);
        icount++;
        if(icount>10) break;
        ++beg;
    }
    
    // 遍历迭代器里面的数据
    beg = vecvalue.begin();
    end = vecvalue.end();
    while(beg != end)
    {
        cout << *beg << endl;
        beg++; // 不要忘记,并且放在循环末尾
    }
    

    (2)灾难程序2 – 容器的释放问题

    vector<int> iv = {100,200,300};
    //…
    for(auto iter = iv.begin();
        iter != iv.end();iter++)
    {
        // erase函数,移除iter位置上的元素,
        // 返回下一个元素位置
        iv.erase(iter);  // 存在问题,直接崩溃
    }
    
    // 稳定版容器释放
    vector<int> iter = iv.begin();
    while(iter != iv.end())
    {
        iter = iv.erase(iter);
    }
    
    // 更简单的释放
    while(!iv.empty())
    {
        // 因为不为空,所以返回的begin()是没问题
        auto iter = iv.begin();
         // 删除该位置上的元素
        iv.erase(iter);
    }
    

    七、范例演示

    (1)用迭代器编译string类型数据

    string str(“I Love China”);
    for(auto iter = str.begin();
        iter != str.end();++iter)
    {
        *iter = toupper(*iter);
    }
    cout << str << endl;
    

    (2)vector容器常用操作及内存释放

    迭代器实战例子

    // ServerName = 1区 // 表示服务器名称
    // ServeID = 100000 // 服务器ID
    struct conf
    {
        char itemname[40];  // 项目名
        itemContext[100];    // 项目内容   
    };
    
    // 根据项目名查询项目内容
    char *getinfo(vector<conf *> &conflict,
            const char *pitem)
    {
        for(auto pos = conflict.begin(); 
              pos != conflict.end(); ++pos)
        {
           if(_strcmp((*pos)->itemname,pitem) == 0) 
           {
                return (*pos)->itemcontext;
           }
        }
        return nullptr;
    }
    
    int main()
    {
        conf *pconf1 = new conf;
        strcpy_s(pconf1->itemname,
                sizeof(pconf1->itemname),
                ”ServerName”);
        strcpy_s(pconf1->itemcontext,
                sizeof(pconf1->itemcontext),
                ”1区”);
    
        conf *pconf2 = new conf;
        strcpy_s(pconf2->itemname,
                sizeof(pconf2->itemname),
                 " ServeID ");
        strcpy_s(pconf2->itemcontext,
                sizeof(pconf2-> itemcontext),
                "100000");
        vector<conf *> conflist;
        conflict.push_back(conf1);// [0]
        conflict.push_back(conf2);// [1]
    
        //strcpy_s(pconf1->itemname,
                sizeof(pconf1->itemname),
                ”ServerName123”);
    
        // 查询项目名字对应的内容
        char *p_tmp = getinfo(conflict,”ServerName”);
        if(p_item != nullptr)
        {
            cout << p_item << endl;
        }
    
        // 释放内存
        std::vector<conf *>::iterator pos;
        for(pos = conflict.begin(); 
                pos != conflict.end(); pos++)
        {
            // *pos代表了conf的指针,
            //删除自己new出来的内存
            delete (*pos); 
        }
        conflict.clear();  // 这个要不要都行。 
        return 0;
    }
    

    好了,今天的C++学习到这里就结束了,喜欢的朋友给我点个赞哦!!!

    相关文章

      网友评论

        本文标题:C++系列 --- 迭代器,失效分析及弥补、实战

        本文链接:https://www.haomeiwen.com/subject/gtbbwqtx.html