string表示可变长的字符序列,vector存放的是某种给定类型对象的可变长序列
3.1命名空间的using声明
作用域操作符::
含义是编译器应从操作符左侧名字所表示的作用域中寻找右侧那个名字。因此std::cin的意思就是要使用命名空间std中的名字cin
有了using声明后就无须专门的前缀(形如命名空间::)
▲每个名字都需要独立的using声明
▲头文件不应包含using声明
▲一点注意事项
3.2标准库类型string
使用string类型必须包含string头文件,string定义在std命名空间中
3.2.1定义和初始化string对象
▲直接初始化和拷贝初始化
使用等号(=)初始化一个变量,实际执行的是拷贝初始化
不使用(=),执行的是直接初始化
3.2.2string对象伤的操作
getline is>>s
▲读写string对象
执行读取操作(cin)时,string对象会自动忽略开头的空白(即空白符 换行符 制表符等等)并从第一个真正的字符开始读起,直到遇到下一处空白为止
▲读取未知数量的string对象
while(cin>>word)
▲使用getline读取一整行
getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止,然后把读入的内容存入那个string对象中.
▲string的empty和size操作
▲string::size_type类型
一个无符号值,足够存放下任何string对象的大小
▲比较string对象
▲为string对象赋值
▲两个string对象相加
▲字面值和string对象相加
3.2.3处理string对象中的字符
在cctype头文件中定义的一组标准库函数
▲处理每个字符?使用基于范围的for语句
范围for语句
这种语句遍历给定序列中的每个元素并对序列中的每个值执行某种操作。
其中,expression部分是一个对象,用于表示一个序列。declaration部分负责定义一个变量,该变量被用于访问序列中的基础元素。每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。
▲使用范围for语句改变字符串中的字符
若想改变string对象中字符的值,必须把循环变量定义成引用类型
当引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上
▲只处理一部分字符
访问string对象中的单个字符有两种方式:
- 下标运算符[]
- 迭代器
▲使用下标执行迭代
▲使用下标执行随机访问
3.3标准库类型vector
使用string类型必须包含string头文件,string定义在std命名空间中
vector是一个类模板,能使用成员函数
vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector,除此之外,大都数(非引用)内置类型和类类型都可以构成vector对象,甚至组成vector的元素也可以是vector
vector<vector<string>> file; //该向量的元素是vector对象
3.3.1 定义和初始化vector对象
·可以默认初始化vector对象,从而创建一个指定类型的空vector
最常见的就是先定义一个空vector,然后当运行时获取到元素的值后再逐一添加
允许把一个vector对象的元素拷贝给另外一个vector对象,但这两个vector对象的类型必须相同
▲列表初始化vector对象
C++提供了几种不同的初始化方式。大多数情况下这些初始化方式可以相互等价地使用
,其中三种例外情况:
- 使用拷贝初始化(=),只能提供一个初始值
- 如果提供一个类内初始值,则只能使用拷贝初始化或使用花括号的形式初始化
- 如果提供的时初始值元素的列表,则只能把初始值都放在花括号里进行列表初始化,而不能放在圆括号里
▲创建指定数量的元素
▲值初始化
只提供vector对象容纳的元素数量而不去略去初始值。这时,库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素
▲列表初始值还是元素数量?
小括号和花括号初始的区别
3.3.2向vector对象中添加元素
vector的成员函数push_back
最有效的方法是先定义一个空的vector对象,再在运行时向其添加具体值
▲向vector对象添加元素蕴含的编程假定
3.3.3其他vector对象的操作
只有当vector中元素的值可比较时,vector对象才可比较
▲计算vector内对象的索引
▲不能使用下标形式添加元素
vector对象(以及string对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素
3.4迭代器介绍
所有标准库容器都可以使用迭代器,但是只有少数几种才同时支持下标运算符
string对象不属于容器类型,但string支持很多与容器类型类似的操作
vector支持下标运算符,这点和string一样
string支持迭代器,这点和vector对象也一样
3.4.1使用迭代器
迭代器的成员函数begin和end
begin成员负责返回并指向第一个元素(或第一个字符)的迭代器
end成员负责返回指向容器(或string对象)“尾元素的下一位置”的迭代器
▲迭代器运算符
*iter iter->mem ++ -- != ==
▲将迭代器从一个元素移动到另外一个元素
end返回的迭代器并不实际指示某个元素,所以不能对其进性递增或解引用的操作
▲迭代器类型
vector<int>::iterator it; //it能读写vector<int>的元素
string::iterator it2; //it2能读写string的字符
vector<int>::const_iterator it3; //it3能读元素,但不能写元素
string::const_iterator it4;//it4只能读字符,不能写字符
const_iterator和常量指针差不多,能读取但不能修改它所指的元素值相反,iterator的对象可读可写
若vector对象或string对象是一个常量,则只能使用const_iterator
若vector对象或string对象不是常量,则既能使用iterator也能使用const_iterator
▲begin和end运算符
begin和end返回的具体类型由对象是否是常量决定,如果对象是常量,begin和end返回const_iterator,如果不是,则返回iterator
cbegin和cend,无论vector对象是否是常量,返回的都是const_iterator
▲给解引用和成员访问操作
解引用迭代器可获得迭代器所指的对象,如果该对象的类型恰好是类,就有可能希望进一步访问它的成员
(*it).empty() //解引用it,然后调用结果对象的empty成员
箭头运算符 –>
it->mem 和 (*it).mem表达的意思相同
3.4.2迭代器运算
vector和string迭代器支持的运算
+ - += -= iter1-iter2 > >= < <=
▲迭代器的算术运算
两个迭代器指向的是同一个容器中的元素或者尾元素的下一位置,就能将其相减,所得结果是两个迭代器间的距离其类型为difference_type
▲和使用迭代器运算
二分搜索法
3.5数组
数组是一种类似于标准库类型vector的数据结构。
与vector相似的地方是,数组也是存放类型相同的对象的容器
与vector不同的地方是,数组的大小确定不变,不能随意向数组中增加元素
如果不清楚元素的确切个数,请使用vector
3.5.1定义和初始化内置数组
数组的维度必须是一个常量表达式
定义数组的时候必须指定数组的类型,不允许用auto关键字由初始值的列表推断类型
和vector对象一样,数组的元素应为对象,因此不存在引用的数组
▲显示初始化数组元素
可以对数组的元素进行列表初始化,允许忽略数组维度,编译器会根据初始值的数量计算并推测出来
▲字符数组的特殊性
可以用字符串字面值对数组初始化
注意字符串字面值的结尾处还有一个空字符
▲不允许拷贝和赋值
不能将数组的内容拷贝给其他数组作为初始值,也不能用数组为其他数组赋值
▲理解复杂的数组声明
数组本身就是对象,所以允许定义数组的指针及数组的引用
理解数组声明的含义,最好的方法是从数组的名字开始由内向外的顺序阅读
3.5.2访问数组元素
当使用数组下标时,通常将其定义为size_t类型。size_t是一种无符号类型,它被定义为足够大以便能表示内存中任意对象的大小。
在cstddef头文件中定义了size_t类型
与vector和string一样,当需要遍历数组的所有元素时,最好的办法是使用范围for语句
▲检查下标的值
3.5.3指针和数组
在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针
一些情况下,数组的操作其实是指针的操作
▲当使用auto变量的初始值时,推断得到的类型是指针而不是数组
其中一层意思是当使用数组作为一个auto变量的初始值时,推断得到的类型是指针而非数组
当使用ia作为初始值时,编译器实际执行的初始化过程类似于下面的形式:
当使用decltype关键字时,返回的类型是由10个整数构成的数组
▲指针也是迭代器
可设法获取尾元素之后那个并不存在的元素的地址
▲标准库函数begin和end
数组不是类类型,因此这个两个函数不是成员函数。正确的使用形式是将数组作为它们的参数
int ia = {0,1,2,3,4,5,6,7,8,9}; //ia是一个含有是10个整数的数组
int *beg = begin(ia); //指向ia首元素的指针
int *last = end(ia); //指向ia尾元素的指针
▲指针运算
支持解引用、递增、比较、与整数相加、两个指针相减
只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一个位置,就能利用关系运算符进行比较。
两个指针相减的结果的类型时一种名为ptrdiff_t的标准库类型,和size_t一样,也是一种定义在cstdeff头文件中的及其相关的类型。因为差值可能为负,所以ptrdiff_t是一种带符号类型。
如果两个指针指向不相关的对象,则不能比较它们。
▲下标和指针
3.5.4 C风格字符串
C风格字符串:字符串存放在字符数组中以空字符结束,意味着最后一个字符后面跟着一个空字符(`\0`)
▲C标准库string函数
strlen strcmp strcat strcpy
传入此类函数的指针必须指向以空字符作为结束的数组
▲比较字符串
比较标准库string对象的时候,使用的是普通的关系运算符和相等性运算符
使用在C风格字符串上,实际比较的将是指针而非字符串本身:
当使用数组的时候其实真正用的是指向数组首元素的指针。因此上面的IF条件实际比较的是两个const char*的值。这两个指针指向的并非同一对象,所以得到未定义的结果。
相比较两个C风格字符串需要调用strcmp函数,此时比较的就不是指针了。如果两个字符串相等,strcmp返回0,如果前面的字符串较大,返回正值;如果后面的字符串较大,返回负值:
▲目标字符串的大小由调用者决定
连接或拷贝C风格字符串也与标准库string对象的同类操作差别很大。
把string对象S1和S2连接起来:
这个操作放在数组上会产生错误。表达式CA1+CA2,试图将两个指针相加,显然这样的曹祖没什么意义,而且肯定是非法的。
正确的方法是使用strcat函数和strcpy函数,。正确的方法是提供一个用于存放结果的数组,该数组必须足够大以便容纳下结果字符串及末尾的空字符。
对大多数应用来说,使用标准库string比使用C风格字符串更安全、更有效
strcpy strcat
3.5.5与旧代码的接口
▲混用string对象和C风格字符串
允许使用字符串字面值来初始化string对象
更一般的情况是,任何出现字符串字面值的地方都可以用以空字符结束的字符数组来替代:
上述性质反过来就不成立了:如果程序的某处需要一个C风格字符串,无法直接用string对象来代替它。
例如,不能用string对象直接初始化指向字符的指针。为了完成这个功能,string专门提供了一个名为c_str的成员函数
能用string对象直接初始化指向字符的指针
c_str函数的返回值是一个C风格的字符串,即返回结果是一个指针,该指针指向以空字符结束的字符数组。指针类型是const char*
▲使用数组初始化vector对象
不允许使用一个数组为另一个内置类型的数组赋值,也不允许使用vector对象初始化数组。相反的,允许用数组来初始化vector对象。
只要指明拷贝区域的首元素地址和尾后地址就可以了
用于初始化vector对象的值也可能仅是数组的一小部分