• STL Vector容器


    STL Vector容器 

    Vector容器简介 

           vector是将元素置于一个动态数组中加以管理的容器。
           vector可以随机存取元素(支持索引值直接存取, 用[]操作符或at()方法,这个等下会详讲)。 
           vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时
           头文件:#include<vector>

    vector对象的默认构造

    vector采用模板类实现,

    vector对象的默认构造形式: vector<T> vecT;

    示例:

    容器中可以添加常规数据类型:

    vector<int> vecInt; //一个存放int的vector容器。
    vector<float> vecFloat; //一个存放float的vector容器。
    vector<string> vecString; //一个存放string的vector容器。

    尖括号内还可以设置指针类型或自定义类型: 

    Class CA{};
    vector<CA*> vecpCA; //用于存放CA对象的指针的vector容器。
    vector<CA> vecCA;
    用于存放CA对象的vector容器。由于容器元素的存放是按值复制的方式进行的,所以此时CA必须提供CA的拷贝构造函数,以保证CA对象间拷贝正常。

    vector中不能存放引用类型!!!!!!!!

    关于 vector 中不能存放引用,这是一个在初始C++时候就应该知道的问题,但是我居然没注意,还好及时发现了。

    《C++ primer》上说 vector 中不能存放引用的原因是:引用不支持一般意义上的赋值操作,而 vector中元素的两个要求是:

    1.元素必须能赋值

    2.元素必须能复制

    1. int a = 1;  
    2. int c =  2;  
    3. int & b = a;  
    4.   
    5. b = c;  

     如上述例子中 ,b = c,不是一般意义上的赋值操作,因为 b 元素时不存在的,对 b 元素取地址,取到的是 a 的地址,

    此处的赋值操作也会将值赋给 a,而不是 b 本身,因此,vector 的元素不能是引用

    我自己试了一下,将发现在 vector 中存放引用报错的原因类似如下:

    1. int  & * p;  

    即,不允许定义引用类型的指针。

    vector末尾的添加移除操作

    理论知识:
           在末尾添加元素: push_back(element)
           在末尾移除元素: pop_back()

    示例:
    vector<int> vecInt; //在容器尾部加入一个元素
    vecInt.push_back(1);
    vecInt.push_back(3);
    vecInt.push_back(5);
    vecInt.push_back(7);
    vecInt.push_back(9);
    //移除容器中最后一个元素
    vecInt.pop_back();
    vecInt.pop_back();

    //打印结果
    1 3 5

    vector的数据存取

    理论知识

    vec.at(idx);   返回索引idx所指的数据,如果idx越界,抛出 out_of_range异常
    vec[idx];   返回索引idx所指的数据,越界时,运行直接报错
    vec.front();   返回第一个元素的引用
    vec.back();   返回最后一个元素的引用

    示例:
    vector<int> vecInt; //假设包含1 ,3 ,5 ,7 ,9
    vecInt.at(2) == vecInt[2] //5
    vecInt.at(2) = 8; 或 vecInt[2] = 8;
    vecInt 就包含 1, 3, 8, 7, 9值

    int iF = vector.front(); //iF==1
    int iB = vector.back(); //iB==9
    vector.front() = 11; //vecInt包含{11,3,8,7,9}
    vector.back() = 19; //vecInt包含{11,3,8,7,19}

    迭代器

    概念

           迭代器是一种检查容器内元素并遍历元素的数据类型。
           标准库为每一种标准容器(包括 vector)定义了一种迭代器类型。
           迭代器 类型提供了比下标操作更通用化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有的容器都适用,现 代 C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下 标操作的 vector 类型也是这样。

    每种容器类型都定义了自己的迭代器类型,如 vector:

    vector<数据类型>::iterator 变量名; //正向迭代器
    Vector<数据类型>::const_iterator 变量名; //只读正向迭代器
    vector<数据类型>::reverse_iterator 变量名; //逆向迭代器
    Vector<数据类型>::const_reverse_iterator 变量名; //只读逆向迭代器

    迭代器可以看做是一个指针

    迭代器的分类

    输入迭代器:
           也有叫法称之为“只读迭代器”,它从容器中读取元素,只能一次读入一个元素向前移动,只支持一遍算法,同一个输入迭代器不能两遍遍历一个序列。(了解)
    输出迭代器:
           也有叫法称之为“只写迭代器”,它往容器中写入元素,只能一次写入一个元素向前移动,只支持一遍算法,同一个输出迭代器不能两遍遍历一个序列。(了解)
    正向迭代器:
           组合输入迭代器和输出迭代器的功能,还可以多次解析一个迭代器指定的位置,可以对一个值进行多次读/写。(了解)
    双向迭代器:
           双向迭代器具有正向迭代器的全部功能。另外它还可以利用自减操作符operator--向后一次移动一个位置。由list容器中返回的迭代器都是双向的。(重点)
    随机访问迭代器:
           具有双向迭代器的所有功能,还可以向前向后跳过任意个位置,可以直接访问容器中任何位置的元素。(重点)

    目前本系列教程所用到的容器,都支持双向迭代器或随机访问迭代器,下面将会详细介绍这两个类别的迭代器。

    双向迭代器与随机访问迭代器

    双向迭代器支持的操作:
    * 自增,自减:it++, ++it, it--, --it
    * 解引用:*it * 赋值:itA = itB,
    * 比较:itA == itB,itA != itB
    其中list, set, multiset, map, multimap支持双向迭代器。

    随机访问迭代器支持的操作:
    * 双向迭代器支持的所有的操作
    * 算数操作:it+=i, it-=i, it+i(或it=it+i) * 随机访问元素:it[i]
    * 关系操作符:itA<itB, itA<=itB, itA>itB, itA>=itB 的功能。
    其中vector,deque支持随机访问迭代器。

    begin 和 end 操作

    每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。如果容器中有元素的话,由 begin 返回的迭代器指向第一个元素
    vector<int>::iterator iter = ivec.begin();
    上述语句把 iter 初始化为由名为 vector 操作返回的值。假设 vector 不空,初始化后, iter 即指该元素为 ivec[0]。
    由 end 操作返回的迭代器指向 vector 的“末端元素的下一个。 我们称之为“超出末端迭代器”(off-the-end iterator) 。表明它指向了一个不存在的元素。如果容器为空,begin 返回的迭代器与 end 返回的迭代器相同

    rbegin 和 rend 操作

    每种容器都定义了一对命名为 rbegin 和 rend 的函数,用于返回逆序迭代器。如 果容器中有元素的话,由 rbegin 返回的迭代器指向容器的最后第一个元素
    vector<int>::reverse_iterator riter = ivec.rbegin();
    上述语句把 riter 初始化为由名为 vector 操作返回的值。假设 vector 不空,初始化后, riter 即指该元素为 riter [size()-1]。 由 rend 操作返回的迭代器指向 vector 的第一个元素前面的位置。表明它指向了一个不存在的元素。如果 容器 为空,rbegin 返回的迭代器与 rend 返回的迭代器相同

    迭代器的自增和解引用运算

    迭代器类型定义了一些操作来获取迭代器所指向的元素,并允许程序员将迭代器从一个元素移动到另一个元素。
    迭代器类型可使用解引用操作符(dereference operator)(*)来访问迭代器所指向的元素:
    *iter = 0;
    解引用操作符返回迭代器当前所指向的元素。假设 iter 指向 vector 对象 ivec 的第一元素,那么 *iter 和 ivec[0] 就是指向同一个元素。上面这个语句的效果就是把这个元素的值赋为 0。
    迭代器使用自增操作符向前移动迭代器指向容器中下一个元素。从逻辑上说,迭代器的自增操作和 int 型对象的自增操作类似。对 int 对象来说,操作结果就是把 int 值“加 1”,而对迭代器对象则是把容器中的迭代器“向前移动一个位置”。因此,如果 iter 指向第一个元素,则 ++iter 指向第二个元素。

    由于 end/rend 操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。 

    vector与迭代器的配合使用

    理论知识:
    vector<T>::iterator 正向迭代器
    vector<T>::reverse_iterator 逆向迭代器
    c.begin() 返回一个迭代器,它指向容器 c 的第一个元素
    c.end() 返回一个迭代器,它指向容器 c 的最后一个元素的 下一个
    c.rbegin() 返回一个逆序迭代器,它指向容器 c 的最后一个元素
    c.rend() 返回一个逆序迭代器,它指向容器 c 的第一个元素前面的位置

    示例:
    vector<int> vecInt; //假设包含1,3,5,7,9元素
    vector<int>::iterator it; //声明容器vector<int>的迭代器。
    it = vecInt.begin(); // *it == 1
    ++it; //或者it++; *it == 3 ,前++的效率比后++的效率高,前++返回引用,后++返回值
    it += 2; //*it == 7
    it = it+1; //*it == 9
    ++it; // it == vecInt.end(); 此时不能再执行*it,会出错!

    正向遍历:

    for(vector<int>::iterator it=vecInt.begin(); it!=vecInt.end(); ++it)
    {
           int iItem = *it;
           cout << iItem; //或直接使用 cout << *it;
    }
    这样子便打印出1 3 5 7 9

    逆向遍历:

    for(vector<int>::reverse_iterator rit=vecInt.rbegin(); rit!=vecInt.rend(); ++rit) //注意,小括号内仍是++rit
    {
           int iItem = *rit;
           cout << iItem; //或直接使用cout << *rit;
    }
    此时将打印出9,7,5,3,1
    注意,这里迭代器的声明采用vector<int>::reverse_iterator,而非vector<int>::iterator。

    迭代器还有其它两种声明方法:
    vector<int>::const_iterator 与 vector<int>::const_reverse_iterator
    以上两种分别是vector<int>::iterator 与vector<int>::reverse_iterator 的只读形式,使用这两种迭代器时,不会修改到容器中的值

    备注:不过容器中的insert和erase方法仅接受这四种类型中的iterator,其它三种不支持。

    《Effective STL》建议我们尽量使用iterator取代const_iterator、reverse_iterator和const_reverse_iterator。

    vector对象的带参数构造

    理论知识

    vector v(beg,end)
    构造函数将[beg, end)区间中的元素拷贝 给本身。注意该区间是左闭右开的区间。

    vector v(n)
    创建有n个初始化元素的容器v

    vector v(n,elem)
    构造函数将n个elem拷贝给本身。

    vector v(const vector &vec)
    拷贝构造函数

    示例:

    int iArray[] = {0,1,2,3,4};
    vector<int> vecIntA( iArray, iArray+5 ); // 0,1,2,3,4

    用构造函数初始化容器vecIntB:
    vector<int> vecIntB ( vecIntA.begin() , vecIntA.end() ); // 0,1,2,3,4
    vector<int> vecIntB ( vecIntA.begin() , vecIntA.begin()+3 ); // 0,1,2

    此代码运行后,容器vecIntB就存放3个元素,每个元素的值是9。
    vector<int> vecIntC(3,9); // 9,9,9

    vector<int> vecIntD(vecIntA); // 0,1,2,3,4

    使用for_each和使用lambda函数来进行遍历, 需要包含<algorithm>头文件
    for_each(vecIntA.begin(), vecIntA.end(), [](const int & val){ cout << val << endl;});

    vector的赋值

    理论知识

    c.assign(b,e)
    重新设置 c 的元素:将迭代器 b 和 e 标记的范围 内所有的元素复制到c 中。 b 和 e 必须不是指向 c 中元素的迭代器。注意该区间是左闭右开的区间。

    c.assign(n,t)
    将容器 c 重新设置为存储 n 个值为 t 的元素。

    c1 = c2
    删除容器 c1 的所有元素, 然后将 c2 的元素复制 给 c1。 c1 和c2的类型(包括容器类型和元素 类型)必须相同。

    c1.swap(c2)
    交换内容:调用完该函数后,c1 中存放的是 c2 原 来的元素,c2变c1。c1 和 c2 的类型必须相同。该函数的执行速度通常要比将 c2 复制到 c1的操作快。

    示例:

    vector<int> vecIntA, vecIntB, vecIntC;
    int iArray[] = {0,1,2,3,4};
    vecIntA.assign(iArray,iArray+5);
    vecIntB.assign( vecIntA.begin(), vecIntA.end() ); //用其它容器的迭代器作参数。
    vecIntC.assign(3,9);
    vector<int> vecIntD;
    vecIntD = vecIntA;
    vecIntA.swap(vecIntD);

    vector的插入

    理论知识
    vec.insert(pos, elem)
    在pos位置插入一个elem元素的拷贝,返回新数据的位置

    vec.insert(pos, n, elem)
    在pos位置插入n个elem数据,无返回值。

    vec.insert(pos, beg, end)
    在pos位置插入[beg,end)区间的数据, 无返回值


    简单案例
    vector<int> vecA;
    vector<int> vecB;
    vecA.push_back(1);
    vecA.push_back(3);
    vecA.push_back(5);
    vecA.push_back(7);
    vecA.push_back(9);
    vecB.push_back(2);
    vecB.push_back(4);
    vecB.push_back(6);
    vecB.push_back(8);
    vecA.insert(vecA.begin(), 11); //{11, 1, 3, 5, 7, 9}
    vecA.insert(vecA.begin()+1,2,33); //{11,33,33,1,3,5,7,9}
    vecA.insert(vecA.begin() , vecB.begin() , vecB.end() ); //{2,4,6,8,11,33,33,1,3,5,7,9}

    vector的删除

    理论知识

    vector.clear()
    移除容器的所有数据

    vec.erase(pos)
    删除pos位置的数据,返回下一个数据的位置。

    vec.erase(beg,end)
    删除[beg,end)区间的数据,返回下一个数据的位置。

    简单案例:

    删除区间内的元素
    vecInt是用vector<int>声明的容器,现已包含按顺序的1,3,5,6,9元素。
    vector<int>::iterator itBegin=vecInt.begin()+1;
    vector<int>::iterator itEnd=vecInt.begin()+2;+
    vecInt.erase(itBegin,itEnd);
    //此时容器vecInt包含按顺序的1,6,9三个元素。

    假设 vecInt 包含1,3,2,3,3,3,4,3,5,3,删除容器中等于3的元素
    for(vector<int>::iterator it=vecInt.being(); it!=vecInt.end(); ) //小括号里不需写 ++it
    {
           if(*it == 3)
           {
                  it = vecInt.erase(it); //以迭代器为参数,删除元素3,并把数据删除 后的下一个元素位置返回给迭代器。此时,不 执行 ++it;
           }
           else
           {
                  ++it;
           }
    }

    //删除vecInt的所有元素
    vecInt.clear(); //容器为空

    小结

           容器vector的具体用法(包括迭代器的具体用法):
           vertor简介
           vector使用之前的准备
           vector对象的构造函数(带参,不带参)
           vector末尾的添加移除操作 vector的数据存取
           迭代器的简介,双向迭代器与随机访问迭代器
           vector与迭代器的配合使用
           vector的赋值
           vector的大小
           vector的插入
           vector的删除。

  • 相关阅读:
    bootstrap 幻灯片(轮播)
    图片轮播
    JS 标签页切换(复杂)
    JS 标签页切换
    js获取日期时间
    HTML标签marquee实现滚动效果
    webstorm 快捷键,很多人想知道的三个常用快捷键
    js红包算法随机分配
    node.js express,ejs后端模板
    递归思路简单例子
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/7790010.html
Copyright © 2020-2023  润新知