STL Vector容器
Posted 云水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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.元素必须能复制
- int a = 1;
- int c = 2;
- int & b = a;
- b = c;
如上述例子中 ,b = c,不是一般意义上的赋值操作,因为 b 元素时不存在的,对 b 元素取地址,取到的是 a 的地址,
此处的赋值操作也会将值赋给 a,而不是 b 本身,因此,vector 的元素不能是引用
我自己试了一下,将发现在 vector 中存放引用报错的原因类似如下:
- 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的删除。
以上是关于STL Vector容器的主要内容,如果未能解决你的问题,请参考以下文章