3.3什么是标准库类型vector
- 作用:标准库类型vector表示对象的集合,其中所有对象的类型都相同。
- 别名--容器:因为vector"容纳着"其他对象,所以vector也被称为容器。
- 头文件:#include<vector>
- 如何访问其中的对象:集合中每个对象都有一个与之对应的索引,所引用于访问对象。
-
类模板( class template):vector是一个类模板。
-
C++中语言既有类模板,也有函数模板。
-
模板本身不是类,或者函数。相反可以将模板看作为编译器生成类或者函数的一份说明。编译器根据模板创建类或者函数的过程称为实例化,当使用模板时,需要指出编译器应把类或者函数实例化为何种类型。
比如:陶瓷碗模具,就可以想成一个模板。这个模具并不是一个碗,不能用来吃饭或者盛东西。但是,我们可以根据这个模具,做一个碗出来。也就时实例化了一个碗。再比如杯子模具等等。
不过C++中的模板相当于 即是陶瓷碗模具,又是被子模具的聚合体,你想要什么,就指定什么类型。
-
-
-
使用方法:
vector<int> ivec; //ivec保存int类型的对象
vector<string> svec; //svec保存string类型的对象
//C++11的写法
vector< vector<string>> file //该向量的元素是vector对象
//C++11以前的写法 外层vector对象的右尖括号和其元素类型之间添加一个空格
vector< vector<string> > file //该向量的元素是vector对象
- 尖括号中必须是对象:因为引用不是对象,所以不存在包含引用的vector。组成vector的元素也可以是vector.
vector是模板而非类,由vector生成的类型必须包含vector中元素的类型。如:vector<int>这才是一个类型
3.3.1定义和初始化vector的对象
初始化vector的对象的方法
vector<T> v1 | v1是一个空vector,他潜在的元素类型是T类型,执行默认初始化 |
vector<T> v2(v1) | v2中包含v1中所有元素的副本 |
vector<T> v2 = v1 | 等价于v2(v1) |
vector<T> v3(n,val ) | v3中包含n个重复的元素,每个元素的值都是val |
vector<T> v4(n) | v4包含了n个重复的执行了值初始化的对象 |
vector<T> v5{a,b,c..} | v5中包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vector<T> v5 = {a,b,c..} | 等价于是上一条。 |
vector<string> svec; //默认初始化,svec中不含任何元素。
看起来空vector没有做任何事情,但其实这是最常用的一种方式:先定义一个空的vector,,运行时获取到元素值后在逐一添加。
-
列表初始化vector对象 (string类型没有列表初始化方式) vector<string> a = {"a","an","the"};
-
C++提供了好几种初始化方法:
-
int unit_sold = 0; //拷贝初始化 int unit_sold = { 0 }; int unit_sold {0}; //列表初始化 int unit_sold(0); //直接初始化 //有何不同点,自己查找。。
-
大多是情况下这些初始化方式可以相互等价使用,不过并非一直如此。
-
使用拷贝初始化时(即使用=时),只能提供一个初始值
-
string s4 = "c"; string s4(n,'c');
-
-
如果提供的是一个类内初始值,则只能使用拷贝初始化或者花括号的形式初始化。
struct Sales_data{ std::string bookNo; //默认初始化 unsigned units_sold = 0; //拷贝初始化 double revenue {0.0}; //花括号 //double revenue (0.0); 编译器报错 };
-
如果提供的是初始元素值的列表,则只能把初始值都放在花括号中进行列表初始化,而不能使用圆括号。
vector<string> v1{"a","an","the"}; //列表初始化 vector<string> v2 ("a","an","the"); //错误
-
-
-
值初始化(value-initialized)
通常情况下,可以只提供vector对象容纳的元素数量而不用考虑初始值。此时库会创建一个值初始化的元素初值,,并把它赋给容器中的所有元素。
例如
vector<int> ivec(10); //10个元素,每个都初始化为0
vector<int> ivec = 10; //错误,只能使用直接初始化形式指定向量大小。
-
列表初始值还是元素数量?
- 圆括号,可以说提供的容量值来构造vector对象
-
花括号,可以说列表初始化vector对象
vector<int> v1(10); //v1有10个元素,每个都是0。用给定的容量值构造vector对象
vector<int> v2{10}; //有一个元素10 初始化vector对象为10
vector<int> v1(10,1); // v3有10个元素,每个都是1
vector<int> v2{10,1}; //两个元素10,1 初始化vector对象为10,1
-
如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了
-
vector<string> v1{10,"hi"}; //有10个值为"hi"的元素
确认无法执行列表初始化后,编译器会尝试默认值初始化vector对象
-
3.3.2 向vector对象中添加元素
-
对vector对象来说,直接初始化( 括号() )的方式适用于三种情况:
- 初始值已知,且数量较少。
- 初始值是另一个vector对象的副本 vector<int> v1(v2);
-
所有元素值都一样 vector<int> v1(10,1); //v1中包含10个1元素
-
·然而更常见的情况是:
- 创建vector对象时,并不知道实际需要的元素个数,元素的值也没办法确定。
- 元素的初值知道,但是值总量较大,而且各不相同。
对于这种情况,添加元素最简单的方法是:利用vector对象的成员函数push_back向其添加元素。
-
push_back负责把一个值当成vector对象的元素"压到(push)"vector对象的"尾端(back)".
例子:
vector<int> v2 //空vector对象 for(int i = 0; i != 100; ++i) v2.push_back(i); //依次把整数值放到c2的末尾
C++标准要求vector在运行时能高效快速的添加元素,因此一开始并没有指定容量大小。事实上,如果一开始就定义了容量大小,还会对性能有所影响。
-
不能通过范围for语句添加或者删除vector对象(或者其他容器)的元素。
-
范围For语句不应该改变其所遍历序列的大小.
-
例如:for( auto beg = v.begin(), end = v.end(); beg != end; ++beg){ auto &r = *beg; //r必须是引用类型,才可以对元素进行写操作 r *= 2; }
在范围for语句中,预存了end()的值。一旦序列中添加(删除)元素,end函数的值就可能变得无效了。
-
-
3.3.3 其他vector操作
支持的操作 | |
v.empty() | 如果V不含有任何元素,返回真;否则返回假 |
v.size() | 返回v中的元素个数 |
v.push_back(t) | 向v的尾端添加一个值为t的元素 |
v[n] | 返回v中第n个位置上元素的索引 |
v1 = v2 | 用v2元素的拷贝替换v1中的元素 |
v1 = {a ,b ,c...} | 用列表中元素的拷贝替换v1中的元素 |
v1 == v2 | v1和v2相等 当且仅当它们的元素数量相同且对应位置元素值相同 |
v1 != v2 | |
< ,<=,> , >= | 以字典顺序进行比较 |
- v.size()
-
vector<int>::size_type //正确
vector::size_type //错误
- 使用下标运算符[]获取指定元素。和string 一样,vector对象的下标也是从0开始记起,下标的类型是相应的size_type类型。
-
不要用下标形式添加元素。
vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。
应该用push_back(t);
vector<int> ivec; //空vector对象 for( decltype(sivec.size()) ix = 0; ix != 10; ++ix) ivec[ix] = ix; //严重错误,ivec不包含任何元素。 //应该改为: ivec.push_back(ix);