第3章 字符串、向量和数组
3.1 命名空间的using声明
using声明形式:using namespace :: name;
一旦声明了上述语句,就可以直接访问命名空间中的名字。例如 using std::cin; 就可以直接使用cin,不用再加上std,但没有声明cout,仍要使用std::cout。每个名字都需要独立的using声明,而且每句话都得以分号结束。
头文件不应包含using声明。因为头文件中的内容会拷贝到所有引用它的文件中去,如果头文件中有using声明,那么每个使用了该头文件的文件都会有这个声明,这可能与与程序中的声明发生冲突。
3.2 标准库类型string
标准库类型string表示可变长的字符序列,使用string类型必须首先包含string头文件。作为标准库的一部分,string定义在命名空间std中。#include <string> using std::string;
3.2.1 定义和初始化string对象
如何初始化类的对象是由类的本身决定的。
- string s1; //默认初始化,s1是一个空串
- string s2 (s1); //s2是s1的副本,二者完全相等
- string s2 = s1; //与上一句等价
- string s3("value"); //s3是字符串字面值"value"的副本,但不包括其最后的空字符
- string s3 = "value"; //与上一句等价
- string s4(n,‘c’); //s4初始化为n个字符c组成的串 只能是重复单引号包围的单个字符,不能为双引号也不能为字符串
如果使用等号= 赋值初始化一个变量,实际上执行的是拷贝初始化(copy initialization);不使用等号赋值,则执行的是直接初始化(direct initialization)
3.2.2 string 对象上的操作
- os << s //将s写到输出流os中,返回os
- is >> s //从is中读取字符串赋给s,字符串以空白分隔,返回is
- getline(is,s) // 从is中读取一行赋给s,返回is
- s.empty() //s为空则返回true,否则返回false,返回的是布尔值
- s.size() //返回s中字符的个数.返回结果的类型并不是int,而是C++中特有的size_type ,定义在string中,全称为string :: size_type 是一个特殊的无符号整型,不要将该数据与int等类型数据混用。可以用auto或decltype来推断其类型。例如auto len = line.size();
- s[n] //返回s中第n个字符的引用,位置n从0算起
- s1 + s2 // 返回s1和s2连接之后的结果
- s1 = s2 // 用s2的副本代替s1中原来的字符
- s1 == s2 s1 != s2// 如果s1和s2所含的字符完全一样,则它们相等,否则不等,对字母的大小写敏感。(长度相同,字符完全相同)
- <,<=,>,>= //利用字符在字典中的顺序进行比较,对字母的大小写敏感。
读写string 对象
使用cin读入,从第一个非空白开始读入,直到遇到下一处空白停止。使用 while (cin >> word),可以反复输入字符。在没有遇到文件结束标记或非法输入时,流有效,可以执行while内部语句操作。
使用getline读取一整行
getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止(换行符被getline读入);然后把所读的内容存入到给定的string对象中,丢弃最后一个换行符(string中不包括换行符)
getline只要一遇到换行符就结束读取操作并返回结果,如果第一个输入换行符,那么所得的结果是个空string
比较string对象
上面介绍了相等性运算符(== 和 !=)下面介绍string对象的关系运算符
- 如果两个string对象的长度不同,而且较短对象 的每个字符都与 较长对象 对应位置上的字符相同,则较短对象 < 较长对象
- 如果两个string对象在某些对应位置上不一样,则两个string对象的比较结果是 第一对相异字符比较的结果(大小写敏感,按照字典顺序比较)
字面值和string对象相加
字符字面值和字符串字面值 与 string对象 是不同的类型,虽然在C语言中二者都是字符串。但是C++中允许字符字面值 和 字符串字面值 转换成 string对象,然后可以与string对象相加。
相加时,要确保每一次相加运算都至少要有一个string对象(两个字符(串)字面值不能直接相加)
3.2.3 处理string对象中的字符
在cctype头文件中定义了一组标准库函数
- isalnum(c) 当c是字母或数字时为真
- isalpha(c) 当c是字母时为真
- iscntrl(c) 当c是控制字符时为真
- isdigit(c) 当c是数字时为真
- isgraph(c) 当c不是空格且可打印时为真
- islower(c) 当c是小写字母时为真
- isprint(c) 当c是可打印字符时为真(即c是空格或c具有可视形式)
- ispunct(c) 当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的任意一种)
- isspace(c) 当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种)
- isupper(c) 当c是大写字母时为真
- isxdigit(c) 当c是十六进制数字时为真
- tolower(c) 如果c是大写字母,输出对应的小写字母;否则原样输出c
- toupper(c) 如果c是小写字母,输出对应的大写字母;否则原样输出c
处理每个字符:
范围for语句(range for) for (declaration : expression) statement
其中expression是一个序列对象,处理字符时一般为一个string对象。declaration处定义一个变量,该变量用于访问序列中的基础元素,每次迭代,该变量会被初始化为序列的下一个元素值。
只处理一部分字符:
使用下标运算符([ ])。下标运算符接收的输入参数是string::size_type 类型的值,这个参数表示要访问的字符的位置;返回值是该位置上字符的引用。下标必须大于等于0并且小于s.size()
任何一个表达式,只要它的值是一个整型值就能作为下标或者索引。如果是带符号类型的值,将会自动转换成无符号整型。
3.3 标准库类型 vector
标准库类型vector表示对象的集合,是一个类模板,其中所有对象的类型都相同。vector容纳着其他对象,也被称为容器container
使用vector必须包含头文件和using声明 #include <vector> using std::vector
vector<int> num; 编译器根据模板vector生成了类型vector<int>. vector能够容纳大多数类型的对象,但引用不是对象,所以不存在包含引用的vector,除此之外,绝大多数(非引用)内置类型和类类型都可以构成vector对象,甚至可以有vector<vector<int>> 早期C++标准中,需要写成vector<vector<int> > 外层vector右尖括号处要加上一个空格,来处理元素为vector的vector对象
/*几种初始化的方法:
拷贝初始化:直接使用赋值号=,右侧可以是值或者对象,但只能提供一个
花括号:花括号内部只能是值,花括号可以与赋值号一起使用,={}
圆括号:圆括号内部可以是对象也可以是值,但都只能是一个(比如圆括号内不能是字符串)。圆括号还有一种特殊功能,(n,val)包含了n个重复的元素,每个元素的值都是val,可以是值或者对象。类似于汇编语言中的n dup(val) */
值初始化:
可以提供数量,或者提供数量和值给vector对象。当只提供数量而不设定初始值时,只能使用直接初始化,不能使用拷贝初始化。
vector圆括号初始化:
- vector<int> v1(10); //v1有10个元素,每个都默认为0
- vector<int> v2(10,1); //v1有10个元素,每个的值都是1
- vector<int> v3("hi"); //错误,圆括号内只能是单个值或单个对象,而且括号内的类型要与vector对象的类型匹配。单个数值优先解释成元素的个数。
- vector<string> v3("hi"); //错误
- vector<string> v3(10,"hi"); //v4有10个元素,每个元素都是字符串"hi"
vector花括号初始化:
- vector<int> v4{10}; //v4有一个元素,值为10
- vector<int> v5{10,2}; //v5有两个元素,分别为10,2
- vector<string> v6{"hi"}; //v6有一个元素
- vector<string> v7{10}; //v7有10个元素,默认为空串
- vector<string> v8{10,"hi"}; //v8有10个元素,值为"hi"
- 花括号优先把值本身赋给vector对象,如果类型不匹配时,就可能将花括号中的数字解释成元素的个数
3.3.2 向vector中添加元素
对vector对象来说,直接初始化只适用于三种情况:初始值已知且较少;初始值是另一个vector对象的副本;所有元素的初始值一样。
可以先创建一个空vector(未初始化即为空,元素个数为0),再利用vector的成员函数push_back(),将一个值当成vector对象的尾元素,压入vector对象的尾端。x.push_back(y); //将y放到x的尾端
3.3.3 其它vector操作
vector的操作大多数和string的相关操作类似
- v.empty(); //如果v不含有任何元素,返回真;否则返回假,是一个布尔值
- v.size(); //返回v中的元素个数,返回类型是由vector定义的size_type类型,例如vector<int>::size_type
- 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
- <,<=,>,>=; //与string类似,是子集时小于,非子集时按字典顺序比较第一个相异元素
访问vector中的元素 与 访问string中的元素类似,使用auto或decltype引用
不能用下标形式添加元素
在C++中,vector对象以及string对象的下标运算符都只能访问已存在的元素,而不能用于添加元素。使用下标访问一个空对象,或者下标为负或者下标溢出,都会发生错误。
3.4 迭代器
迭代器类似于指针,提供了对对象的间接访问。迭代器有有效和无效之分,这一点也和指针差不多。有效迭代器要么指向某个元素,要么指向容器中尾元素的下一位置;其他所有情况都属于无效。
auto b = v.begin(),e = v.end(); //b和e的类型相同,begin返回指向容器第一个元素的迭代器。end返回指向容器“尾元素的下一位置”的迭代器,是一个本不存在的“尾后”元素,没有实际意义,仅仅是个标记,所以不能对其进行递增递减或解引用操作;end成员返回的迭代器被称作尾后迭代器或尾迭代器。如果容器为空,则begin和end返回的是同一个迭代器。一般来说,我们并不清楚也不在意迭代器的准确类型是什么。
迭代器运算符
- *iter //返回迭代器iter所指元素的引用
- iter -> mem //解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem;其中->为箭头运算符
- ++iter //令iter指向容器中的下一个元素
- --iter //令iter指向容器中的上一个元素
- iter1 == iter2 //判断两个迭代器是否相等(不相等),如果两个迭代器指向的是同一个元素;或者它们是同一个容器的尾后迭代器,则二者相等,反之不等。
- iter1 != iter2
所有标准库容器的迭代器都定义了==和!=,但是它们中的大多数都没有定义>或<运算符,在与迭代器相关的循环体中,使用==和!=是一个好习惯。
迭代器类型
标准库类型中的迭代器类型用iterator和const_iterator来表示。例如vector<int>::iterator it; //it能读写vector<int>的元素 vector<int>::const_iterator it; //it只能读不能写
const_iterator和常量指针类似,能读取值但不能通过常量指针修改值。如果vector对象是常量,只能使用const_iterator;如果不是常量,那么既能使用iterator也能使用const_iterator,但不能通过const_iterator修改值。
begin和end运算符
begin和end返回的具体类型由对象是否常量决定,如果对象是常量,begin和end返回const_iterator;如果不是常量,则返回iterator
C++11中引入了两个新函数,cbegin()和cend,不论对象本身是否常量,cbegin和cend都返回常量迭代器。
某些vector对象的操作会使迭代器失效,谨记,但凡是使用的迭代器的循环体,都不要向迭代器所属的容器中添加元素。
3.4.2 迭代器运算
所有标准库容器都有支持递增运算的迭代器,也能用==和!=对任意标准库类型的两个有效迭代器进行比较。string和vector的迭代器还支持每次移动跨过多个元素,也支持迭代器进行关系运算,这些运算统称为迭代器运算。
string和vector的迭代器支持的运算:
- iter + n //迭代器加上一个整数值仍为一个迭代器,迭代器指示的新位置比原来向前移动了若干个元素。结果迭代器要么指示容器内的一个元素,要么只是容器尾元素的下一位置。
- iter - n //迭代器减去一个整数值仍为一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器要么指示容器内的一个元素,要么指示容器尾元素的下一位置。
- iter += n //将iter + n的结果赋给iter
- iter -= n //将iter - n的结果赋给iter
- iter1 - iter2 //两个迭代器相减的结果是它们之间的距离。右侧迭代器向前移动差值个元素后,得到左侧迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。(属于同一个容器),结果可为正负零。与迭代器和位置操作相关的数值,类型名difference_type,这个类型名在string和vector中都有定义
- >、>=、<、<= //迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器指向的容器位置之前,则说明前者小于后者,其他类似,都与指向元素的位置相关。参与运算的两个迭代器必须属于同一个容器。
3.5 数组