C++菜鸟学习笔记系列(8)
本期主题:标准库类型vector
在上一篇C++菜鸟学习笔记系列(7)博客中我们对标准库类型 string 进行了相关介绍。vector也是一种非常重要的标准库类型,在这一篇博客中作者将从定义和初始化、添加元素、使用索引等方面来介绍一些关于C++语言标准库类型 vector 的使用方法。
标准库类型 vector 表示对象的集合,其中所有对象的类型都相同。与string类型相似的,集合中的每个对象都有一个与之对应的索引,索引可以用于访问对象。因为“ vector”中存放着其他对象,所以它也常常被称为容器(后面我们再做详细介绍)
我们在使用 vector 类型时也要包含相应的头文件,这里我们使用:
#include <vector>
1. 定义和初始化vector对象
vector能够容纳绝大多数类型的对象作为其元素,同时也可以容纳我们自定义的类型对象。由此我们可以得到我们在C++菜鸟学习笔记系列(4)中所介绍的引用并不是对象,所以不存在包含引用的 vector 。除此之外, vector 本身也是一个对象,所以其还可以作为自己的元素。
下面我们来看一下如何定义和初始化一个vector对象:
vector <T> v1;
这是我们定义一个vector对象最常用的方式,v1是一个空的vector,它的潜在元素时T类型,执行默认初始化,初始化的值会由T的类型决定,例如T为int类型则初始化的值为0,如果T为string类型则默认为“”。
我们看起来空 vector 对象没有什么用,但是很快我们会知道C++程序在运行时可以很高效的往 vector 对象中添加元素。事实上我们并没有必要在创建vector 对象时初始化好元素,相反的创建一个空 vector 对象,然后再向其添加元素是一种非常常见的使用方式。
列表初始化vector对象
C++11新标准为我们提供了一种为vector对象的元素赋初值的方法,即猎鸟初始化。此时用{}括起来的0个或多个初始元素值就被赋给vector对象。
vector <int> v2 {1, 2, 3}; vector <string> v3 { "123", "456", "789" };
上面的代码表示我们初始化了两个vector对象其中 v2 表示含有1 2 3三个int元素, v3 表示含有"123" “456” "789"三个string元素。
注意:上面的花括号{}不能使用圆括号()代替。
还有一种情况是我们在初始化过程中所要初始化的值是多个重复的,这时候如果我们还是像上述的方法一样去重复多个赋值的话就会显得特别冗余。由此我们就要引申出给定容量值构造初始化的方法。
给定容量值构造初始化
vector <int> v4 (10,1); vector <string> v5 (10,"ha");
简单描述一下上述代码,第一行10代表所定义vector对象v4中容纳的元素 1 的个数。第二行也是类似的。
通常情况下我们还可以略去初始值而只提供vector对象容纳元素的数量。此时库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。当然,这个初值由vector对象中的元素类型所决定。
vector <int> v4 (10); vector <string> v5 (10);
我们把初始化的代码改成如上所示,则代表v4对象中有10个0,v5对象中有10个空字符串“”。但是对于上述定义方法如果对象中的元素类型不支持默认初始化,我们就必须提供初始值,否则只提供元素的数量不提供初始值无法完成初始化的工作。
某些情况下,初始化的真实含义还取决于传递值时使用的是花括号还是圆括号。例如:
vector <int> v6 (10 , 1);// v6有10个元素,每个值都是1 vector <int> v7 {10 , 1};// v7有2个元素,一个值为10,另一个值为1
如上所示,如果是使用了圆括号我们认为是构造初始化花括号我们则认为是列表初始化。
注意:关于这一方面还有很多注意点,很冗长,这里就不过多介绍了。
2.向vector对象中添加元素
我们在第一节中已经提到创建一个空的vector对象然后再向其中添加元素值是一个非常常见的使用方式。这是很有道理的,例如我们想要创建一个vector对象包含从1-99的整型数,那我们总不能在定义初始化的时候就把1-99这99个整型数全部输入进去吧。
C++语言为我们提供了vector对象中的成员函数push_back,push_back负责把一个值当做是vector对象的尾元素“压到”vector对象的“尾端”。
下面我们来看一个小例子:
/* Author: wxc_1998 Date: 2018/10/1 */ #include <iostream> #include <vector> #include <string> using namespace std; void main() { string word; vector <string> v1; while (cin >> word) { v1.push_back( word ); } cout << "over!!!" << endl; cout << "press any key to continue!"; cin.clear(); cin.sync(); cin.get(); }
通过上述代码我们可以看到,我们每次循环通过变量word向vector对象中添加一个 string 类型的元素。
3. vector对象中的索引
和string类型相似的,我们也可以通过下标运算符获取到指定的 vector 元素。vector 对象的下标也是从0开始记起,且其值必须小于vector.size()。
下面我们看一个小例子:
/* Author: wxc_1998 Date: 2018/10/1 */ #include <iostream> #include <vector> using namespace std; void main() { vector <int> v; cout << "please input the data(int):" << endl; int i; while (cin >> i) { v.push_back(i); } for (int j = 0; j < v.size(); j++ ) { v[j] *= v[j]; } cout << "the square of each element : " << endl; for (int k= 0; k < v.size(); k++ ) { cout << v[k] << " "; } cout << endl << "press any key to continue!"; cin.clear(); cin.sync(); cin.get(); }
上述代码我们实现了输入一组整型数,输出每个数的平方值的功能,由代码我们可以看到对于vector对象的索引使用。
输出结果为:
索引的使用可以让我们方便的去访问vector对象中的每一个元素,我们可以通过索引对vector对象访问、赋值等操作,但是我们最好不要试图通过索引为vector对象添加元素。例如下面所示的代码:
vector <int> v; for (int i = 0 ;i < 10 ; i++ ) { v[i] = i; }
我们看上去没有什么语法错误,但是我们仔细分析一下可以知道:vector对象v在创建时是一个空的对象,里面没有任何元素,然后我们在for循环体中却又使用下标去访问v[i],这不是一个明显的错误吗?这种错误是不会被编译器发现的,而是会产生一个不可预知的值。
这种通过下标访问不存在的行为非常常见,而且会产生很严重的后果。所谓的缓冲区溢出就是指这种错误,这也是导致PC机及其他设备上应用程序出现安全问题的一个重要原因。
(引自:C++primer)
所以我们在使用索引时一定要注意下标运算符只能用于访问已经存在的元素,而不能用于添加元素。
好了,这次就写到这里了,我们下次再见。
祝大家国庆节快乐!愿祖国繁荣昌盛!
注:虽然这篇博客的内容十分简单,但是大家若有转载还请标明出处!
还有大家若对博客中的内容有任何问题可以随时联系我提问。