第三章 字符串、向量和数组
# 第三章 字符串、向量和数组
# using声明
- 使用某个命名空间:例如
using std::cin表示使用命名空间std中的名字cin。 - 头文件中不应该包含
using声明。这样使用了该头文件的源码也会使用这个声明,会带来风险。
# string
- 标准库类型
string表示可变长的字符序列。 #include <string>,然后using std::string;- string对象:注意,不同于字符串字面值。
# 定义和初始化string对象
初始化string对象的方式:
| 方式 | 解释 |
|---|---|
string s1 | 默认初始化,s1是个空字符串 |
string s2(s1) | s2是s1的副本 |
string s2 = s1 | 等价于s2(s1),s2是s1的副本 |
string s3("value") | s3是字面值“value”的副本,除了字面值最后的那个空字符外 |
string s3 = "value" | 等价于s3("value"),s3是字面值"value"的副本 |
string s4(n, 'c') | 把s4初始化为由连续n个字符c组成的串 |
- 拷贝初始化(copy initialization):使用等号
=将一个已有的对象拷贝到正在创建的对象。 - 直接初始化(direct initialization):通过括号给对象赋值。
# string对象上的操作
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中字符的个数 |
s[n] | 返回s中第n个字符的引用,位置n从0计起 |
s1+s2 | 返回s1和s2连接后的结果 |
s1=s2 | 用s2的副本代替s1中原来的字符 |
s1==s2 | 如果s1和s2中所含的字符完全一样,则它们相等;string对象的相等性判断对字母的大小写敏感 |
s1!=s2 | 同上 |
<, <=, >, >= | 利用字符在字典中的顺序进行比较,且对字母的大小写敏感(对第一个不相同的位置进行比较) |
- string io:
- 执行读操作
>>:忽略掉开头的空白(包括空格、换行符和制表符),直到遇到下一处空白为止。 getline:读取一整行,包括空白符。
- 执行读操作
s.size()返回的时string::size_type类型,记住是一个无符号类型的值,不要和int混用s1+s2使用时,保证至少一侧是string类型。string s1 = "hello" + "world" // 错误,两侧均为字符串字面值- 字符串字面值和string是不同的类型。
# 处理string对象中的字符
- ctype.h vs. cctype:C++修改了c的标准库,名称为去掉
.h,前面加c。如c++版本为
cctype,c版本为ctype.h- 尽量使用c++版本的头文件,即
cctype
- 尽量使用c++版本的头文件,即
cctype头文件中定义了一组标准函数:
| 函数 | 解释 |
|---|---|
isalnum(c) | 当c是字母或数字时为真 |
isalpha(c) | 当c是字母时为真 |
iscntrl(c) | 当c是控制字符时为真 |
isdigit(c) | 当c是数字时为真 |
isgraph(c) | 当c不是空格但可以打印时为真 |
islower(c) | 当c是小写字母时为真 |
isprint(c) | 当c是可打印字符时为真 |
ispunct(c) | 当c是标点符号时为真 |
isspace(c) | 当c是空白时为真(空格、横向制表符、纵向制表符、回车符、换行符、进纸符) |
isupper(c) | 当c是大写字母时为真 |
isxdigit(c) | 当c是十六进制数字时为真 |
tolower(c) | 当c是大写字母,输出对应的小写字母;否则原样输出c |
toupper(c) | 当c是小写字母,输出对应的大写字母;否则原样输出c |
- 遍历字符串:使用范围for(range for)语句:
for (auto c: str),或者for (auto &c: str)使用引用直接改变字符串中的字符。 (C++11) str[x],[]输入参数为string::size_type类型,给出int整型也会自动转化为该类型
# vector
- vector是一个容器,也是一个类模板;
#include <vector>然后using std::vector;- 容器:包含其他对象。
- 类模板:本身不是类,但可以实例化instantiation出一个类。
vector是一个模板,vector<int>是一个类型。 - 通过将类型放在类模板名称后面的尖括号中来指定类型,如
vector<int> ivec。
# 定义和初始化vector对象
初始化vector对象的方法
| 方法 | 解释 |
|---|---|
vector<T> v1 | v1是一个空vector,它潜在的元素是T类型的,执行默认初始化 |
vector<T> v2(v1) | v2中包含有v1所有元素的副本 |
vector<T> v2 = v1 | 等价于v2(v1),v2中包含v1所有元素的副本 |
vector<T> v3(n, val) | v3包含了n个重复的元素,每个元素的值都是val |
vector<T> v4(n) | v4包含了n个重复地执行了值初始化的对象 |
vector<T> v5{a, b, c...} | v5包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vector<T> v5={a, b, c...} | 等价于v5{a, b, c...} |
- 列表初始化:
vector<string> v{"a", "an", "the"};(C++11)
# 向vector对象中添加元素
v.push_back(e)在尾部增加元素。
# 其他vector操作
vector支持的操作:
| 操作 | 解释 |
|---|---|
v.emtpy() | 如果v不含有任何元素,返回真;否则返回假 |
v.size() | 返回v中元素的个数 |
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 | 同上 |
<,<=,>, >= | 以字典顺序进行比较 |
- 范围
for语句内不应该改变其遍历序列的大小。 vector对象(以及string对象)的下标运算符,只能对确知已存在的元素执行下标操作,不能用于添加元素。
# 迭代器iterator
- 所有标准库容器都可以使用迭代器。
- 类似于指针类型,迭代器也提供了对对象的间接访问。
# 使用迭代器
vector<int>::iterator iter。auto b = v.begin();返回指向第一个元素的迭代器。auto e = v.end();返回指向最后一个元素的下一个(哨兵,尾后,one past the end)的迭代器(off the end)。- 如果容器为空,
begin()和end()返回的是同一个迭代器,都是尾后迭代器。 - 使用解引用符
*访问迭代器指向的元素。 - 养成使用迭代器和
!=的习惯(泛型编程)。 - 容器:可以包含其他对象;但所有的对象必须类型相同。
- 迭代器(iterator):每种标准容器都有自己的迭代器。
C++倾向于用迭代器而不是下标遍历元素。 - const_iterator:只能读取容器内元素不能改变。
- 箭头运算符: 解引用 + 成员访问,
it->mem等价于(*it).mem - 谨记:但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。
标准容器迭代器的运算符:
| 运算符 | 解释 |
|---|---|
*iter | 返回迭代器iter所指向的元素的引用 |
iter->mem | 等价于(*iter).mem |
++iter | 令iter指示容器中的下一个元素 |
--iter | 令iter指示容器中的上一个元素 |
iter1 == iter2 | 判断两个迭代器是否相等 |
# 迭代器运算
vector和string迭代器支持的运算:
| 运算符 | 解释 |
|---|---|
iter + n | 迭代器加上一个整数值仍得到一个迭代器,迭代器指示的新位置和原来相比向前移动了若干个元素。结果迭代器或者指示容器内的一个元素,或者指示容器尾元素的下一位置。 |
iter - n | 迭代器减去一个整数仍得到一个迭代器,迭代器指示的新位置比原来向后移动了若干个元素。结果迭代器或者指向容器内的一个元素,或者指示容器尾元素的下一位置。 |
iter1 += n | 迭代器加法的复合赋值语句,将iter1加n的结果赋给iter1 |
iter1 -= n | 迭代器减法的复合赋值语句,将iter2减n的结果赋给iter1 |
iter1 - iter2 | 两个迭代器相减的结果是它们之间的距离,也就是说,将运算符右侧的迭代器向前移动差值个元素后得到左侧的迭代器。参与运算的两个迭代器必须指向的是同一个容器中的元素或者尾元素的下一位置。 |
>、>=、<、<= | 迭代器的关系运算符,如果某迭代器指向的容器位置在另一个迭代器所指位置之前,则说明前者小于后者。参与运算的两个迭代器必须指向的是同一个容器中的元素或尾元素的下一位置。 |
- difference_type:保证足够大以存储任何两个迭代器对象间的距离,可正可负。
# 数组
- 相当于vector的低级版,长度固定。
# 定义和初始化内置数组
- 初始化:
char input_buffer[buffer_size];,长度必须是const表达式,或者不写,让编译器自己推断。 - 数组不允许直接赋值给另一个数组。
# 访问数组元素
- 数组下标的类型:
size_t。 - 字符数组的特殊性:结尾处有一个空字符,如
char a[] = "hello";。 - 用数组初始化
vector:int a[] = {1,2,3,4,5}; vector<int> v(begin(a), end(a));。
# 数组和指针
- 使用数组时,编译器一般会把它转换成指针。
- 标准库类型限定使用的下标必须是无符号类型,而内置的下标可以处理负值。
- 指针访问数组:在表达式中使用数组名时,名字会自动转换成指向数组的第一个元素的指针。
# C风格字符串
- 从C继承来的字符串。
- 用空字符结束(
\0)。 - 对大多数应用来说,使用标准库
string比使用C风格字符串更安全、更高效。 - 获取
string中的cstring:const char *str = s.c_str();。
C标准库String函数,定义在<cstring> 中:
| 函数 | 介绍 |
|---|---|
strlen(p) | 返回p的长度,空字符不计算在内 |
strcmp(p1, p2) | 比较p1和p2的相等性。如果p1==p2,返回0;如果p1>p2,返回一个正值;如果p1<p2,返回一个负值。 |
strcat(p1, p2) | 将p2附加到p1之后,返回p1 |
strcpy(p1, p2) | 将p2拷贝给p1,返回p1 |
尽量使用vector和迭代器,少用数组
# 多维数组
- 多维数组的初始化:
int ia[3][4] = {{0,1,2,3}, ...}。 - 使用范围for语句时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。
# 指针vs引用
- 引用总是指向某个对象,定义引用时没有初始化是错的。
- 给引用赋值,修改的是该引用所关联的对象的值,而不是让引用和另一个对象相关联。
# 指向指针的指针
- 定义:
int **ppi = π - 解引用:
**ppi
# 动态数组
- 使用
new和delete表达和c中malloc和free类似的功能,即在堆(自由存储区)中分配存储空间。 - 定义:
int *pia = new int[10];10可以被一个变量替代。 - 释放:
delete [] pia;,注意不要忘记[]。
编辑 (opens new window)