Vector容器
标准库类型vector表示对象的集合,其中所有对象的类型相同,且有一个与之对应的索引。要想使用vector,必须包含适当的头文件 #include
注意!!!
vector是模版而非类型,由vector生成的类型必须包含vector中的类型,例如:
vector<int> ivec; //ivec 保存 int 类型的对象
vector<Sales_item> Sales_vec; //保存Sales_item类型的对象
vector<vector<string>> file; //该向量的元素是vector对象
vector 能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。
还有在早期版本的C++标准中对于上述第三条代码的用法有所不同,过去必须在外层vector对象的右尖符号和其元素类型之间添加一个空格,如应该写成:
vector<vector<string> > file;
定义和初始化vector
关于一些常用的初始化vector对象方法:
方法 | 解释 |
---|---|
vector< T > v1 | v1是一个空vector,他潜在的元素是T类型,执行默认初始化 |
vector< T > v2(v1) | v2包含有v1所有元素的副本 |
vector< T > v2 = v1 | 等价于v2(v1) , v2中包含有v1所有元素的副本 |
vector< T > v3(n, val) | v3包含了n个重复的元素,每个元素的值都是val |
vector< T > v4(n) | v4包含了n个重复地执行了值初始化的对象 |
vecto< T > v5{a,b,c…} | v5包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vecto< T >v5={a,b,c…} | 等价于v5{a,b,c…} |
关于它的列表初始值还是元素数量
在某些情况下,初始化的真实含义依赖于传递初始值时用的是花括号还是圆括号。当用== 圆括号 的时候,它的初始值代表着它的 容量 ;当为 花括号 时,它的初始值代表它的 元素的初值 ==。
当然也有特殊情况,在花括号中,如果提供的值不能用来列表初始化,就要考虑用这样的值来构造vector对象了。例如:
vector<string> v5{"h1"}; //列表初始化,v5有一个元素hi
vector<string> v5{"h1"}; //错误:不能使用字符串字面值构建vector 对象
vector<string> v7{10}; //v7有10个默认初始化的元素,
vector<string> v8{10,"hi"}; //v8有10个值为“hi”的元素
上述的例子只有第一条是列表初始化。第三条因为不能用 int 初始化string对象,所以v7和v8提供的值不能作为元素的初始值,无法执行列表初始化后,只能尝试用默认值初始化vector对象。
向vector对象中添加元素
一.运用push_back函数向其中添加元素
push_back函数负责把一个值当成vector对象的尾元素“压缩到(push)”一个vector对象的“尾端(back)”。例如:
string word;
vector<string> text; //空vector对象
while (cin>> word){
text.push_back(word);
} //把word添加到text后面
二.使用下标形式添加元素
它也能和数组一样使用下标来访问容器里的数据,但是前提是在定义容器时,需要初始化容器的容量,否则会报错,例如:
string word;
vector<string> text(10);
for(int i=0;i<10;i++)
{
cin>>word;
text[i] = word;
}
关于访问vector中的元素
访问vector的元素方法和字符串一样,也是通过字符串元素在vector对象中的位置,例如:
vector (auto &i : v) //这里运用引用是为了能改变vector对像中元素的值
i*= i;
for (auto i :v) //这里是单纯访问v中的每个元素,并不对里面的元素做修改。
cout<<i<<" ";
cout<<endl;
迭代器
我们已知的除了可以用下标运算符来访问string对象的字符或vector对象的元素,还有就是迭代器。所有标准库容器都可以使用迭代器,但是其中只有少数几种才能同时使用下标和迭代器,所以我们还需要掌握迭代器。
关于迭代器的具体类型,我们不需要了解清楚,就像不知道string和vector中的size_type成员一样,在运用的时候统一用auto来获取迭代器的类型,例如:
auto a=v.begin(), b=v.end(); //a代表v中的第一个元素,b代表v中的第二个类型,a与b都是相同类型
在拥有迭代器的标准库类型使用iterator和const_iterator 来表示迭代器的类型(前者是可读写,后者是只可读,跟const一样)
vector<int>::iterator it; //it能读写vector<int>的元素
string::iterator it2; //it2能读写string的元素
vector<int>::const_iterator it3; //it3只能读vector<int>的元素,不能写元素
string::iterator it4; //it4同样只能读元素,而不能写元素
迭代器运算符操作
迭代器的解引用和成员访问操作
迭代器的解引用理解和指针差不多,对他解引用就是访问所指对象的值,例如判断一个迭代器所指的字符串是否为空:
(*it).empty() //解引用it,然后调用结果对象的empty函数,判断是否为空
*it.empty(); //错误,试图访问it的名为empty的成员,但it是个迭代器,没有empty成员
所以从上述代码中,我们还需要注意*的运算顺序,记得要加括号!
除了解引用,C++还定义了箭头运算符(->).意思是把解引用和成员访问两个操作两个结合在一起,也就是说 it->mem和(*it).mem表达的意思是一样的。
例如:假如用一个名为text的字符串向量存放文本文件的数据,其中的元素或者是一句话或者是一个用于表示段落分隔的空字符串。如果要输出text中第一段的内容,可以利用迭代器写一个循环令其遍历text,直到遇到空字符串为止:
//依次输出的text的每一行直至遇到第一个空白行为止
for(auto it=next.cbgein(); it !=text.cend()&&!it->empty();++it)
cout<< *it<<endl;
注意,vector对象虽然可以动态地增长,但是也会有一些副作用,已知的一个限制是不能在范围for循环中向vector对象添加元素。另一个限制是任何一种可能改变vector对象容量的操作,比如push_back,都会使该vector对象的迭代器失效。
加减运算与逻辑运算
和下标运算差不多, 一个迭代器加n:表示向后移n个元素;
减n则代表向前移n个元素;
iter1+=n:则代表将iter1 加n的结果赋给iter1;
两个迭代器相减,则代表两个迭代器的距离,类型是difference_type,一种带符号的整型数;
“>、>=、<、<=”:如果迭代器所指的容器位置在另一个位置之前,则说明前者小于后者,后面的以此类推。
begin运算符和end运算符
一些有迭代器类型的容器同时拥有返回迭代器的成员。
比如begin和end的成员时,
begin成员就是负责返回指向第一个元素或(第一个字符)的迭代器。
end成员返回的迭代器通常被称作“尾后迭代器”或者简称尾迭代器 ,即返回尾元素的下一个位置的迭代器,也就是说该迭代器指示的是容器一个本不存在的“尾后”元素。这样的迭代器其实没什么实际含义,仅是个标记,表示我们处理完了容器中的所有元素。
当容器为空的时候他们返回的是同一个迭代器。
因为迭代器有const类型的,所以我们还拥有cbegin和cend来专门得到const_iterator类型的返回值。