文章目录
- 5 字符串、向量和数组
- 5.1 命名空间
- 5.2 标准库string
- 5.2.1 定义和初始化string对象
- 5.2.2 string对象上的操作
- 5.2.2.1 读取string对象
- 5.2.2.2 风格
- 5.2.2.3 使用getline读取一整行
- 5.2.2.4 empty和size操作
- 5.2.2.5 size_type类型
- 5.2.2.6 比较string对象
- 5.2.2.7 string对象的相加
- 5.2.2.8 字面值和string对象相加
- 5.2.3 处理每个字符
- 5.2.3.1 范围for语句
- 5.2.3.2 下标
- 5.3 一维数组
- 5.3.1 一维数组的定义方式
- 5.3.2 数组的初始化规则
- 5.3.3 一维数组数组名
- 5.3.4 练习案例1:五只小猪称体重
- 5.3.5 练习案例2:元素逆置
- 5.3.6 冒泡排序
- 5.4 二维数组
- 5.4.1 二维数组的定义方式
- 5.4.2 二维数组的数组名
- 5.4.3 二维数组应用案例
- 5.5 标准库vector
- 5.5.1 实例化
- 5.5.2 定义和初始化vector对象
- 5.5.3 向vector对象中添加元素
- 5.5.4 案例
- 5.5.5 其他的操作
- 5.5.5.1 访问所有元素
- 5.5.5.2 检查空和大小
- 5.5.5.3 索引
5 字符串、向量和数组
除了第二章我们介绍的基本数据类型,实际上C++里还定义了一个内容丰富的抽象数据类型库。其中string
和vector
库是两种最重要的标准库类型,前者支持可变长的字符串,相当于char数组的加强版。后者支持可变长的集合,相当于数组的加强版。除此之外,还有一种标准库类型是迭代器,它通常和string和vector配套使用,用于访问它们里面的元素。
5.1 命名空间
在第一章中我们曾经谈到过这个问题,实际上,在目前的代码中我们大多数都会在代码中加入以下的代码,用于声明我们要用的所有东西都从std库中取。
using namespace std;
以上的声明方法可以使得全局在访问std库中的内容。
需要注意的问题
我们后面会谈到C++的代码可以分文件书写,以体现优美性。而我们通常在头文件中是不会书写using声明的,因为一旦某个文件中导入使用了using声明的头文件,则会导致这个文件也会带有using声明。在实际开发中,我们常常使用各种库的工具,有些工具重名会导致情况很复杂。
5.2 标准库string
标准库string表示可变长的字符序列。如果你想要在代码中使用string这个抽象类型,那么你需要导入它的头文件。
#include <string>
如果你想要使用string的方法,你可以通过一下的方式去调用它。
std::string::
5.2.1 定义和初始化string对象
对于字符串string类型,我们可以有以下几种初始化方式。
string s1;//默认初始化,s1是一个空字符串
string s2 = s1;//s2是s1的副本
string s3 = "字面值";//s3是该字符串字面值的副本
string s4(10,'c');//s4的内容是cccccccccc
我们需要认识一个点。string库实际上是一个类。如果你学过java等面向对象编程语言,你就可以理解上面的s4为何可以采用那种方式初始化。本质上那种方法是把参数传入有参构造器中,我们称为直接初始化
;我们把不采用有参构造器初始化而采用=
来初始化的方法称为拷贝初始化
。
5.2.2 string对象上的操作
一个类除了规定初始化的方式外,还要定义对象上所能执行的操作。我们在Java时常叫做方法,而在C++中通常称为函数。
我们试着来体会几种常用的函数操作。
5.2.2.1 读取string对象
在第二讲中我们曾经谈过,cin可以用于从键盘中读取字符,当然,读取完的字符我们可以读入s。
试着敲一下下面的代码,体会我说的话。
#include <iostream>
#include <string>
using namespace std;int main()
{string s;cout << "请输入字符串:" << endl;cin >> s;cout << "输入的字符串为:" << s << endl;system("pause");return 0;
}
out:
我们需要知道一个事,string对象是不会读取空格符、换行符、制表符这种特殊符号的,所以string对象是要从第一个实字符开始读取,直到遇到空格符等符号时停止。
还是上面的代码,我们试着输入hello world,在输入hello时,当遇见hello后面的空格,后半部分的world实际上未被输入到world对象中。
out:
需要注意的是,虽然用cin时string对象不会读取空格符等符号,但是如果是字面值的话含有的空格string对象是可以识别的。
#include <iostream>
#include <string>
using namespace std;int main()
{string s = "hello";string s2 = " world";cout << "输出:" << s << s2 << endl;system("pause");return 0;
}
out:
5.2.2.2 风格
我想我们说的不够清楚。在这一小节中,我们着重讲解C风格字符串和C++字符串的区别。
字符串型用于表示一串字符,其包含下面两种风格:
- C风格字符串:char 变量名[] = “字符串值”;
- C++风格字符串:string 变量名 = “字符串值”
如果使用C风格字符串来表示一串字符串的话:
char dog[8] = { 'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; // not a string!
char cat[8] = {'f', 'a', 't', 'e', 's', 's', 'a', '\0'}; // a string!
因为在C++规定中,C风格字符串都是以空字符结尾,空字符被写作\0,其用来标记字符串的结尾。上面两个都是char数组,但实际上,只有第二个数组是字符串,原因如同我们前面所说。cout显示上面的cat前面的七个字符,发现空字符后停止;如果不添加空字符,比如dog,那么cout会把内存中随后的各个字节全部打印出来,直至遇到空字符才会停止;所以,空字符是很重要的。
但是,如果用C风格字符串的数组形式来表示字符串的话,那么要写如此多的单引号,还要加空字符,他不烦我都烦,所以为了大家都省心,后面C++又出现了一种只需用引号括起来即可的字符串,这种字符串被称为字符串常量
或字符串字面值
。
char bird[11] = "Mr. Cheeps"; // the \0 is understood
char fish[] = "Bubbles"; // let the compiler count
用引号括起的字符串隐式地
包括结尾的空字符,因此不用你再去写出来了。
用图形来表示一下我前面所说的意思就是:
当然了,不管显示隐式,你需要知道的是,用sizeof查看这种方式写出来的字符串大小的时候,他是会把空字符给算进去的,如下图:
注意,我们说过字符常量
用的是单引号,不能用双引号;同样地,字符串常量
只能用双引号,不能用单引号。
如果要解释的话,你要知道的是字符常量一般来说用单引号括起的某一个字符都是对于ASCII的某一个数字,而我们写字符串常量用双引号括起来一般都是对应字符串所在的内存地址。所以,如果你写出如下的代码:
char shirt_size = 'S';
那只不过是把ASCII中的83赋给了shirt_size,由于是字符型常量所以变成了字符;而如果你是写出如下代码:
char shirt_size = "S";
那么这个实际上是:把"S"的内存地址赋给了shirt_size。两者的意思完全不同。
所以,当走到C++来的时候,char字符串类型是可以和string字符串类型画上等号的,string字符串中也是隐式地含有一个\0。
5.2.2.3 使用getline读取一整行
有时我们希望我们通过键盘输入的字符串中保留输入时的空白符,这时候用getline函数代替原来的cin就可以解决这个问题。
getline函数只会读取一行的内容,也就是说,它的结束判别方式不是空格符等符号决定的,而是换行符决定的。为此,如果你在输入中加入空格,并不会使得它输出。
#include <iostream>
#include <string>
using namespace std;int main()
{string line;getline(cin, line);cout << line << endl;system("pause");return 0;
}
out:
5.2.2.4 empty和size操作
从名字上看,我们大概可以猜出empty和size的功能。empty可以判断string是否为空,size可以判断string的大小。
用法很简单,调用对象的函数即可。
#include <iostream>
#include <string>
using namespace std;int main()
{string line = "hello";cout << line.empty() << endl;system("pause");return 0;
}
out:
对于empty来说,其返回的是一个布尔值。
让我们再来看看size的用法。
#include <iostream>
#include <string>
using namespace std;int main()
{string line = "hello";cout << line.size() << endl;system("pause");return 0;
}
out:
5.2.2.5 size_type类型
5.2.2.6 比较string对象
string类中定义了几个运算符用于比较string对象。
最开始要介绍的运算符即为==
和!=
。它们可以检验两个string对象相等或不相等。
5.2.2.7 string对象的相加
string对象相加只需要简单的用+就行了。如下所示:
#include <iostream>
#include <string>
using namespace std;int main()
{string str1 = "hello";string str2 = "world";string str3 = str1 + str2;cout << "两个字符串拼接后的结果:" << str3 << endl;system("pause");return 0;
}
out:
5.2.2.8 字面值和string对象相加
字面值也可以直接和string对象用+
相加,但是有一点需要保证的是+的两侧必须有一个是string对象,字面值和字面值是不能通过+简单的相加的。
敲一下下面的代码,理解上面我所说的话。
#include <iostream>
#include <string>
using namespace std;int main()
{string str1 = "hello";string str2 = "world";string str3 = str1 + "big" + str2;cout << "两个字符串拼接后的结果:" << str3 << endl;system("pause");return 0;
}
out:
5.2.3 处理每个字符
5.2.2中是对整个string对象做操作,而在这一小节中,我们打算对字符串中的每个字符做操作。
5.2.3.1 范围for语句
C++11中为我们提供了范围for语句用于遍历字符串序列中每个元素,这种用法类似于Java中的增强for循环。其语法形式如下:
for(取值对象:遍历对象)执行操作
在以下的代码中,让我们仔细体会一下其用法:
#include <iostream>
#include <string>
using namespace std;int main()
{string str1 = "I love you more than i can say";for (auto c : str1) {cout << c << endl;}system("pause");return 0;
}
out:
我们试着对每个字符做一些操作如何?
在下面的例子中,我们想要把字符串改写为大写字母的形式,为此我们使用了标准库函数toupper,这个函数每次只能接收一个字符,然后输出其对应的大写形式。需要注意的是,由于返回的数值要影响原来的str1对象,故我们要使用引用&。
#include <iostream>
#include <string>
using namespace std;int main()
{string str1 = "I love you more than i can say";for (auto &c : str1) {c = toupper(c);//将每个小写字符转换为大写字符}cout << "大写的str1:" << str1 << endl;system("pause");return 0;
}
out:
5.2.3.2 下标
我们不想对string对象中所有的字符做操作,而是想要对某些字符做操作,那么我们可以使用[]
来访问string对象中的字符元素,如果你学过python,你会对此很熟悉,元素索引从0开始。
让我们试着下面的代码,我们准备把love中的l变为大写L:
#include <iostream>
#include <string>
using namespace std;int main()
{string str1 = "I love you more than i can say";str1[2] = toupper(str1[2]);cout << "大写的str1:" << str1 << endl;system("pause");return 0;
}
out:
越界问题
使用下标时总要注意不能越界,即下标一定是从0开始,但是在size的范围内,如果超出size,那问题就很大了宝贝。
在Java中数组如果索引越界会抛出异常,程序报错,而对于C++来说,其标准并不要求检测下标是否合法,一旦你使用了一个超出范围的下标,其结果是不可预知的。
5.3 一维数组
5.3.1 一维数组的定义方式
要创建数组,可使用声明语句。数组声明应指出以下三点:
- 存储在每个元素中的值的类型
- 数组名
- 数组中的元素数
一维数组定义的三种方式:
数据类型 数组名 [数组长度];
数据类型 数组名 [数组长度] = {值1,值2...};
数据类型 数组名[] = {值1,值2...};
让编译器去做
像第三种定义方式那样让编译器计算元素个数是一种很糟糕的做法,因为其计数可能和我们自己想的不太一样。
数组的特点:
- 放在一块连续的内存空间中
- 数组中每个元素都是相同的数据类型
数组的索引:
数组的用途都是基于这样一个事实:可以单独访问数组元素。方法是使用下标或索引来对元素进行编号。C++的数组从0开始编号,这没有商量的余地。即arr[0]
。
有效下标值的重要性:
编译器不会检查使用的下标是否有效。例如,如果将一个值赋给不存在的元素,如
int arr[10] = {1,2,3,4,5,6,7,8,9,10} cout<<arr[11]<<endl;
那么编译器是不会指出错误的。
但是在程序运行后,这种赋值可能引发问题,它可能破坏数据或代码,也可能导致程序异常终止,所以必须确保程序只使用有效的下标值。
示例:
#include <iostream>
using namespace std;
int main()
{/*1、数据类型 数组名[数组长度]; 2、数据类型 数组名[数组长度] = { 值1,值2... }; 3、数据类型 数组名[] = { 值1,值2... }; *///1、数据类型 数组名[数组长度]//int arr[5];//给数组中的元素进行赋值//数组元素的下标是从0开始索引的/*arr[0] = 10;arr[1] = 20;arr[2] = 30;arr[3] = 40;arr[4] = 50;*///访问数据元素/*cout << arr[0] << endl;cout << arr[1] << endl;cout << arr[2] << endl;cout << arr[3] << endl;cout << arr[4] << endl;*///2、数据类型 数组名[数组长度] = {值1,值2...}//如果在初始化数据的时候,没有把数据全部初始化,那么没有初始化的值初始值为0int arr2[5] = { 10,20,30,40,50 };/*cout << arr2[0] << endl;cout << arr2[1] << endl;cout << arr2[2] << endl;cout << arr2[3] << endl;cout << arr2[4] << endl;*/for (int i = 0; i < 5; i++) {cout << arr2[i] << endl;}//3、数据类型 数组名[] = { 值1,值2... };int arr3[] = { 100,90,80,70,60,50,40,30,20,10 };for (int i = 0; i < 10; i++) {cout << arr3[i] << endl;}system("pause");return 0;
}
【总结1:数组名的命名规范与变量名命名规范一致,不要和变量重名】
5.3.2 数组的初始化规则
C++有几条初始化数组的规则,它们限制了初始化的时刻,决定了数组的元素数目和初始化器中值得数目不相同时将发送的情况。
一般来说,我们给数组定义的时候都会顺便初始化,即:
int cards[4] = {3,6,8,10}
但是也有例外,不过,如果你在定义的时候不初始化,后面就没法初始化了,如果是在方括号里面指定数组的元素个数那还好说,你不初始化它还会给你默认填0,;但是如果你连数组的元素个数都没指定,那数组就要蒙了:我是谁,我在干嘛,它一无所知。所以为了让你在定义的时候也要做初始化的工作,vistual studio做得非常好,如下:
值得一提的是,C++不像java,你不能说把数组赋给另外一个数组,这是不允许的。
int cards[4] = {3,6,8,10};
int hand[4];
hand = cards;
在编译器里,编译器会提示你表达式必须是可修改的左值。
如果你在初始化数组的时候,你指定了数组元素的个数,但是却没有完全初始化,只初始化一部分的值,那么此时其他未指定初始化值的元素都会默认为0.
5.3.3 一维数组数组名
一维数组名称的用途:
- 可以统计整个数组在内存中的长度
sizeof(arr)
- 可以获取数组在内存中的首地址
cout<<arr<<endl
示例:
#include <iostream>
using namespace std;
int main()
{//数组名用途//1、可以通过数组名统计整个数组占有的内存大小int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };cout << "整个数组名的占用空间为:" << sizeof(arr) << endl;cout << "每个元素占用内存空间为:" << sizeof(arr[0]) << endl;cout << "数组中元素的个数为:" << sizeof(arr) / sizeof(arr[0]) << endl;//2、可以通过数组名查看数组首地址cout << "数组首地址为:" << (int)arr << endl;cout << "数组中第一个元素地址为:" << (int)&arr[0] << endl;cout << "数组中第二个元素地址为:" << (int)&arr[1] << endl;//数组名是常量不可以进行赋值操作//arr = 10;system("pause");return 0;
}
【注:其实arr
一般说的就是&arr[0]
,即你指一个数组,一般就是指数组的头元素的地址】
5.3.4 练习案例1:五只小猪称体重
案例描述:
在一个数组中记录了五只小猪的体重,如int arr[5] = {300,350,200,400,250}
,找出并打印最重的小猪体重。
核心思想:
示例:
#include <iostream>
using namespace std;
int main()
{int arr[5] = { 300,350,200,400,250 };int max = 0;int test = 0;for (int i = 0; i < 5; i++) {test = arr[i];if (max < test) {max = test;}}cout << "五只小猪里最重的小猪体重为:" << max << endl;system("pause");return 0;
}
5.3.5 练习案例2:元素逆置
案例描述:
请声明一个5元素的数组,并且将元素逆置。(如原数组元素为:1,3,2,5,4;逆置后输出结果为:4,5,2,3,1)
核心思想:
示例:
#include <iostream>
using namespace std;
int main()
{//1、创建数组前int arr[5] = { 1,3,2,5,4 };int start = 0;int end = sizeof(arr) / sizeof(arr[0]) - 1;cout << "逆置前的数组为:" << endl;for (int i = 0; i < 5; i++) {cout << arr[i] << endl;}//创建中间变量放元素int temp = 0;//实现逆置while (start<end) {temp = arr[start];arr[start] = arr[end];arr[end] = temp;start++;end--;}cout << "逆置后的数组为:" << endl;for (int i = 0; i < 5; i++) {cout << arr[i] << endl;}system("pause");return 0;
}
5.3.6 冒泡排序
作用:最常用的排序算法,对数组内元素进行排序
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个
- 对每一对相邻元素做同样的工作,执行完毕后,找到一个最大值
- 重复以上的步骤,每次比较次数-1,直到不需要比较
核心思想:
示例:将数组{4,2,8,0,5,7,1,3,9}进行升序排序
#include <iostream>
using namespace std;
int main()
{//利用冒泡排序实现升序排列int arr[9] = { 4,2,8,0,5,7,1,3,9 };cout << "排序前:" << endl;for (int i = 0; i < 9; i++){cout << arr[i] << " ";}cout << endl;//开始冒泡排序//总共排序轮数为 元素个数-1for (int i = 0; i < 9 - 1; i++) {//内层循环对比for (int j = 0; i < 9 - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[ j + 1 ] = temp;}}}//排序后结果cout << "排序后:" << endl;for (int i = 0; i < 9; i++){cout << arr[i] << " ";}cout << endl;system("pause");return 0;
}
5.4 二维数组
二维数组就是在一维数组上,添加一个维度。
5.4.1 二维数组的定义方式
二维数组定义的四种方式:
数据类型 数组名[行数][列数];
数据类型 数组名[行数][列数] = {数据1,数据2},{数据3,数据4};
数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4};
数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4}
【建议:以上4种定义方式,利用第二种更加直观,提高代码的可读性。】
示例:
#include <iostream>
using namespace std;int main()
{//1、数据类型 数组名[行数][列数]int arr[2][3];arr[0][0] = 1;arr[0][1] = 2;arr[0][2] = 3;arr[1][0] = 4;arr[1][1] = 5;arr[1][2] = 6;/*cout << arr[0][0] << endl;cout << arr[0][1] << endl;cout << arr[0][2] << endl;cout << arr[1][0] << endl;cout << arr[1][1] << endl;cout << arr[1][2] << endl;*/for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {cout << arr[i][j] << " ";}cout << endl;}//2、数据类型 数组名[行数][列数] = {数据1,数据2},{数据3,数据4}int arr2[2][3] = { {1,2,3} ,{4,5,6} };for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){cout << arr2[i][j] << " ";}cout << endl;}//3、数据类型 数组名[行数][列数] = {数据1,数据2,数据3,数据4}int arr3[2][3] = { 1,2,3,4,5,6 };for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){cout << arr3[i][j] << " ";}cout << endl;}//4、数据类型 数组名[][列数] = {数据1,数据2,数据3,数据4}int arr4[][3] = { 1,2,3,4,5,6 };for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){cout << arr4[i][j] << " ";}cout << endl;}system("pause");return 0;
}
5.4.2 二维数组的数组名
- 查看二维数组所占内存的空间
- 获取二维数组的首地址
示例:
#include <iostream>
using namespace std;int main()
{//1、查看占用内存空间大小int arr[2][3] = { {1,2,3} ,{4,5,6} };cout << "二维数组占用的内存空间为:" << sizeof(arr) << endl;cout << "二维数组第一行占用空间为:" << sizeof(arr[0]) << endl;cout << "二维数组第一个元素占用内存为:" << sizeof(arr[0][0]) << endl;//通过上面几条代码可以统计二维数组的行数和列数cout << "二维数组的行数为:" << sizeof(arr) / sizeof(arr[0]) << endl;cout << "二维数组的列数为:" << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;//2、获取二维数组的首地址cout << "二维数组的首地址:" << (int)arr << endl;cout << "二维数组第一行首地址为:" << (int)arr[0] << endl;cout << "二维数组第二行首地址为:" << (int)arr[1] << endl;cout << "二维数组第一个元素的首地址为:" << (int)&arr[0][0] << endl;cout << "二维数组第二个元素的首地址为:" << (int)&arr[0][1] << endl;system("pause");return 0;
}
5.4.3 二维数组应用案例
考试成绩统计
案例描述:有三名同学(张三,李四,王五),在一次考试中的成绩分别如下表,请分别输出三名同学的总成绩。
张三 | 100 | 100 | 100 |
李四 | 90 | 50 | 100 |
王五 | 60 | 70 | 80 |
示例:
#include <iostream>
#include <string>
using namespace std;int main()
{//二维数组案例-考试成绩案例//1、创建二维数组int scores[3][3] = {{100,100,100},{90,50,100},{60,70,80}};string students[3] = { "张三","李四","王五" };//2、统计每个人的总和分数for (int i = 0; i < 3; i++) {int sum = 0;//统计每个人的分数总和for (int j = 0; j < 3; j++) {sum += scores[i][j];/*cout << scores[i][j] << " ";*/}cout << students[i]<<"的总分为:"<<sum << endl;}system("pause");return 0;
}
5.5 标准库vector
vector类似于java中的Array和List,它比数组的功能强,可供使用的成员函数函数更多。
vector每个对象都有索引,索引可以用于访问每个对象,vector本身也是个对象,故我们通常称vector对象为容器。
要想使用vector,与string同理,也需要导入头文件。
#include <vector>
5.5.1 实例化
C++语言既有类模板,也有函数模板,模板是什么我们后续会讲,但是需要知道的是,vector就是一个类模板。
模板本身不是类或函数,但是可以看做是类或函数的说明。而编译器根据模板创建类或函数的过程称为实例化。当使用模板时,需要之处编译器应该把类或函数实例化哪种类型。
可能你对我说的是啥不是很清楚。我提一个问题:如果你想在想要一个能装int对象的容器,那你一定要说明对吧?说明要装int对象,那么该容器就是int类型容器。如下所示:
vector<int> ivec;//ivec容器中可以存放int对象
vector<Scales_item> Sales_vec;//Sales_vec容器中可以存放Scales_item对象
vector<vector<string>> file;//file容器中的元素是容器。里面的容器又放的是字符串。
5.5.2 定义和初始化vector对象
vector初始化有很多种方式,最简单的莫过于声明但不赋值,这样编译器就会默认给你一个空容器。
vector <string> svec;//默认初始化,svec不包含任何元素
C++的容器模板提供了许多方法,可以帮助我们简便地为vector对象添加元素。
当然,我们也可以指定初始值,或者通过拷贝都是可以的,不过需要注意的是,指定的初始值和拷贝操作都需要元素和容器类型对应。
如何指定初始值?C++提供了一种给vector对象的元素赋予初始值的方法,我们称之为列表初始化
。其语法格式如下:
vector<string> articles = {"a","an","the"};
如果是拷贝的话:
vector<string> articles = {"a","an","the"};
vector<string> articles2 = (articles);//将articles的元素全部拷给articles2,需要注意的是容器类型必须相同!
当然你也可以指定容器中的元素,并且要填充多少个这个元素。
vector<int> v1(10,-1);//创建10个int类型的元素,每个元素都初始化为-1
如果你是按照以上的方式去初始化容器的,当你没有给出初始化的值而指定创建多少个类型的元素,那么系统会按照默认给出这几个元素,如:
vector<int> v1(10);//默认给出10个int元素,每个int元素都是0
vector<string> v2(10);//默认给出10个string元素,每个string元素都是空字符串
5.5.3 向vector对象中添加元素
有时候,我们并不确定容器中存有多少元素。为此,我们可以先创建一个空容器,然后用成员函数push_back
向容器内添加元素,这就是为什么vector对象比数组好用的原因。
vector<int> v1;//空容器对象
for(int i = 0;i != 100;++i)v1.push_back(i);
有时也可以实时读入数据然后赋予vecotr对象:
string word;
vector<string> text;
while(cin >> word)
{text.push_back(word);//把word添加到text后面
}
vector对象
相比于静态数组和动态数组来说,vector拥有更好的效能。在数组中我们曾经学过,使用静态数组时要指定数组的大小,这就为后续添加元素造成了不便,而使用new开辟空间来创建动态数组又需要指针的控制,这并不好用。
5.5.4 案例
案例要求:编写一段程序,用cin输入一组整数并把它们存入一个vector对象。
#include <iostream>
#include <string>
#include <vector>
using namespace std;int main()
{//1 定义一个空容器vector<int> v1;//2 用cin循环读入一组整数int a;while (cin >> a) {v1.push_back(a);}
}
5.5.5 其他的操作
除了push_back操作外,vector还有许多操作和string类似,我们来体会一下这些操作并学会使用它们!
5.5.5.1 访问所有元素
通过范围for可以访问容器中所有的元素。
#include <iostream>
#include <string>
#include <vector>
using namespace std;int main()
{//1 定义一个空容器vector<int> v1 = {1,2,3,4,5,6};//2 使用范围for输出所有元素for (auto i : v1) {cout << i;}cout << endl;}
out:
5.5.5.2 检查空和大小
vector容器可以使用empty和size两个成员函数来判断容器中是否为空和容器的大小。具体的使用在string中已经详细说明,如果想要复习的可以试着敲一下下面的代码。
#include <iostream>
#include <string>
#include <vector>
using namespace std;int main()
{//1 定义一个空容器vector<int> v1 = {1,2,3,4,5,6};//2 检查容器的大小cout << v1.size() << endl;//3 检查容器是否为空cout << v1.empty() << endl;}
out:
5.5.5.3 索引
和string一样,只要容器不加上const限定符,他就能够通过索引来改变容器内的值。
但是有些人可能想耍小聪明,想利用下标索引来添加元素,这是不允许的。下标索引只能用于访问容器中已经存在的元素。
缓冲区溢出
通过下标访问不存在的元素会产生严重的后果,这个后果叫做缓冲器溢出,其使得设备和PC上总会出现安全问题。
当然,缓冲区溢出常常和程序员的粗心有关,如果你不能保证自己能够使用正确的下标索引,那么使用范围for是保证不会报错的重要手段。