目录
标准库类型vector
c++中既有类模板也有函数模板,vector就是一个类模板
1.类模板
1.1类模板概念
模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。
引用类模板需要引头文件
#include<vector>
且不同模板需要引的头文件也不同
1.2模板实例化
想要将模板示例化成具体的类,需要我们提供一些额外的信息指定到底成什么类,提供信息的方式是:在模板名字后加一对尖括号,在括号内填入信息。
vector<int> s1 //s1中保存int类型的对象
vector<string> s2 //s2中保存string类型的对象
vector<s_item> s3 //s3中保存s_item类的对象
vector<vector<string>> s4 //该向量的元素是vector对象
注意:
1.vector是模板而非类型,其生成的类型必须包含vector中元素类型,如vector<int>
2.引用不是对象,所以不存在包含引用的vector
3.一些老式编译器无法识别vector<vector<int>>,只能识别vector<vector<int> >(后两个尖括号间有空格)
2.定义与初始化vector对象
2.1 定义vector对象
常用方法:
最常见的方式是先定义一个空的vector,然后运行时获取元素值再逐一添加
如果要把一个vector容器拷贝到另一个里,那么两个容器对象类型必须一致,此时新容器是原容器的副本
vector<int> s1;
//添加一些元素
vector<int> s2(s1) //把s1中元素拷贝到s2
vector<int> s3=s1 //把s1中元素拷贝到s3
vector<string> s4(s2) //错误,对象不一致
2.2列表初始化vector对象
C++11新标准还提供了另外一种为vector对象的元素赋初值的方法,即列表初始化。此时,用花括号括起来的0个或多个初始元素值被赋给vector对象:
vector<string> s={"a","aa","aaa"};
此时,s1中有三个元素a aa aaa
如果提供的是初始值,那么只能放在花括号中
vector<string> s={"a","aa","aaa"}; //正确
vector<string> s=("a","aa","aaa"); //错误
创建指定数量的元素
vector<int> s=(10,1); //创建10个1
vector<string> s=(10,“hi”); //创建是个hi
值初始化
通常情况下,可以只提供vector对象容纳的元素数量而不用略去初始值。此时库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。这个初值由vector对象中元素的类型决定。
vector<int> s=(10); //10个0
vector<string> s=(10); //10个空string对象
元素个数与初始化值
在某些情况下,初始化的真实含义依赖于传递初始值时用的是花括号还是圆括号。例如,用一个整数来初始化vector<int>时,整数的含义可能是vector对象的容量也可能是元素的值。类似的,用两个整数来初始化vector<int>时,这两个整数可能一个是vector对象的容量,另一个是元素的初值,也可能它们是容量为2的vector对象中两个元素的初值。通过使用花括号或圆括号可以区分上述这些含义:
vector<int> v1 (10); // v1有10个元素,每个的值都是0
vector<int> V2{10}; // v2有1个元素,该元素的值是10
vector<int> v3(10,1); //v3有10个元素,每个的值都是1
vector<int> v4{10,1}; //v4有2个元素,值分别是10和1
如果初始化时使用了花括号的形式但是提供的值又不能用来列表初始化,就要考虑用这样的值来构造vector对象了
vector<string> v5{ "hi"}; //列表初始化:v5有一个元素
vector<string> v6("hi"); //错误:不能使用字符串字面值构建vector对象
vector<string> V7{10}; //v7有10个默认初始化的元素
vector<string> V8{10,"hi"}; // v8有10个值为"hi"的元素
可以这么理解:当内部元素类型不一致时,花括号初始化默认先按赋值处理,当格式合适时,按小括号处理。
3.向vector容器中添加元素
直接初始化的方式其实并不方便,因此,我们往往会先创建一个空vector容器然后再像容器中依次加数据
3.1push_back函数
vector的成员函数push_back能向其中添加元素,push_back负责把一个值当成vector对象的尾元素“压到(push)”vector对象的“尾端(back)”
vector<int> v2; //空vector对象
for (int i = 0; i != 100; ++i)
v2.push_back(i); //依次把整数值放到v2尾端
//循环结束后v2有100个元素,值从0到99
如果在运行时我们才知道元素个数也能如此
//从标准输入中读取单词,将其作为vector对象的元素存储
string word;
vector<string> text; // 空vector对象
while (cin >> word){
text.push _back (word); //把word添加到text后面
}
为何vector能做到如此呢?
3.2vector容器底层
vector数据结构和数组十分相似,也称其为单端数组
vector与数组区别:
数组是静态空间,而vector是动态拓展
动态拓展:
并不是在原空间上连续接新空间,而是找更大的空间将原数据拷贝到新空间,释放原空间
vector的迭代器支持随机访问
4.其他vector操作
除push_back外,vector还提供了其他操作,大多数与string相关操作类似。
访问对象的方法与string差不多
vector<int> V{1,2,3,4,5,6,7,8,9};
for (auto &i : v) //对于v中的每个元素(注意:i是一个引用)
i*= i; //求元素值的平方
for (auto i : v) //对于v中的每个元素
cout << i <<" "; //输出该元素
cout<<endl;
vector的empty与size和string里的功能完全一致
注意:
1.不能用下标的形式添加元素
vector<int> s1; for(decltype(s1.size() i=0;i!=10;++i) s1[i]=i //错误,s1中不含任何元素
2.用下标访问时不要越界
试图用下标的形式去访问一个不存在的元素将引发错误,不过这种错误不会被编译器发现,而是在运行时产生一个不可预知的值。
这种通过下标访问不存在的元素的行为非常常见,而且会产生很严重的后果。所谓的缓冲区溢出(buffer overflow)指的就是这类错误,这也是导致PC及其他设备上应用程序出现安全问题的一个重要原因。