Skip to main content

第 3 章:字符串、向量和数组

本章重点:stringvector

string 表示可变长的字符序列

vector 存放的是某种给定类型对象的可变长序列

3.1 命名空间的 using 声明

using namespace::name;

头文件不应包含 using 声明,因为头文件的内容会拷贝到所有引用它的文件中去

3.2 标准库类型 string

#include <string>

using std::string;

w定义和初始化 string 对象

string 对象上的操作

输入输出

输入将会忽略开头的空白(空格、换行),从第一个字符开始,直到遇到下一个空白。如输入 “ hello world! ”,输出 “hello”。

getline

getline 函数返回的那个换行符实际上被丢弃掉了

string::size_type

size 方法返回的不是 int 或 unsigned ,而是 string::size_type是一个无符号整数型,能够存放任何 string 的大小。需要注意的是 size() < n当 n 是 负数时,结果大概率是 true,n 会被转为一个 无符号值。

字符串字面值

字符串字面值是为了兼容 C 的,字符串字面值和 string 是不同的类型

string 可以与字符串字面值相加,得到新的 string。但是字符串字面值不能与字符串字面值相加。

处理 string 中的字符串

基于范围的 for 语句

for (declaration: expression)
statement
for (auto c : str) {
cout << c << endl;
}

通过引用改变值

for (auto &c : str) {
c = toupper(c);
}

下标运算符

s[0] 是第一个字符

s[s.size() - 1] 是最后一个字符

下标必须大于等于 0,而小于 size() 的值,可以设置下标类型为:string::size_type

其他

如何退出 while(cin >> str) 循环

键入 ctrl + d 跳出循环

3.3 标准库类型 vector

vector 表示对象的集合,每个对象的类型都相同,并有一一对应的索引。

#include <vector>

using std::vector;

vector 是模板而非类型,由 vector 生成的类型必须包含 vector 中元素的类型,例如 vector<int>

定义和初始化 vector 对象

列表初始化 vector 对象

vector<string> arcticles = { "a", "an", "the" };

值初始化

10 个元素,每个都是空 string 对象。

vector<string> ivec(10);

向 vector 对象添加元素

push_back 负责向 vector 末尾添加值。

vector 对象能高效增长。

注意:范围 for 语句中最好不要有改变遍历序列大小的操作。

其他 vector 操作

empty、size 跟 string 功能完全一致,size 返回 vector<T>::size_type

不能用下标形式添加元素

向一个空的 vector 直接 vec[idx] = xxx 是错误的,下标运算符用于访问已存在的元素。

确保下标合法的一种有效手段就是尽可能使用范围for语句。

3.4 迭代器

使用迭代器

v.begin(); // 指向第一个元素
v.end(); // 指向尾元素的下一个位置

end() 返回的迭代器称为尾后迭代器(off-the-end iterator, end iterator)。

如果容器为空,他们指向同一个位置,都是尾后迭代器。

迭代器运算符

迭代器的 for 循环,使用 != 判断循环结束条件,有些迭代器不支持 <

for (auto is = s.begin(); is != s.end() && !isspace(*it); ++it) {
*it = toupper(*it);
}

迭代器类型

vector<int>::iterator it;
strinng::iterator it2;

vector<int>::const_iterator itConst;
strinng::const_iterator itconst2;

一般无需关系迭代器类型,直接 auto 即可。如果对象是常量,那么就会得到 xxx::const_iterator

有时候默认行为不是想要的,可以通过 cbegin() cend() 得到 xxx::const_iterator

解引用、成员访问操作

(*it).empty() 必须要加括号。

it->empty() 使用箭头运算符,结合解引用和成员访问。

注意:凡是使用迭代器的循环体,都不要向容器添加元素。

迭代器运算

3.5 数组

数组类似于 vector,但是大小固定,不能随意增加。

定义和初始化数组

数组维度必须是常量表达式

int arr_1[10];
constexpr unsigned len = 42;
int arr_2[len];

数组的内部的某类型的元素,同内置类型的变量一样默认初始化为未定义的值。

显示的初始化数组

int arr_3[3] = {0, 1, 2}; // 维度是 3
int arr_4[] = {0, 1, 2}; // 维度是 3
int arr_5[5] = {0, 1, 2}; // 维度是 5,相当于 {0, 1, 2, 0, 0}
string arr_6[3] = {"hello"} // 维度是 3, 相当于 {"hello", "", ""}

初始值大于长度会报错。

字符数组的特殊性

需要注意的是:字符串字面值的结尾处还有一个空字符。

char a2[] = {'C', '+', '+'}; // 长度为 3
char a3[] = "C++"; // 长度为 4
char a4[3] = "C++"; // 报错,长度不足

不允许拷贝和赋值

不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。

复杂的数组声明

int *ptrs[10]; //   ptrs 是一个含有 10 个整形指针,int* ptrs[10] 这样写可能更好理解
int &refs[10] = /* ? */ // 错误:不存在引用的数组
int (*Parray)[10] = &arr; // Parray 指向一个含有 10 个整数的数组
int (&arrRef)[10] = arr; // arrRef 引用一个含有 10 个整数的数组

int *(&ptrsRef)[10] = ptrs; // ptrsRef 引用一个含有 10 个整形指针的数组

访问数组元素

数组的下标类型为 size_t

下标越界试图访问非法内存区域,也会产生错误。

指针和数组

大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针

int arr[] = {0, 1, 2};
// 下面三个操纵等价
int *p1 = &arr[0];
int *p2 = arr;
auto *p3 = arr;

指针也是迭代器

指向数组元素的执政还支持 迭代器的运算

int arr[] = {0, 1, 2};
int *p = arr; // 指向 arr[0]
++p; // 指向 arr[1]

得到尾后指针

int *lp = &arr[3]; // 注意:arr 实际的最后一个下标是 2, 而这种方式可以拿到尾后指针

使用标准库方法获得指针:begin(arr) end(arr)

需要特别注意的,尾后指针不能执行解引用和递增操作。

下标

内置的下标运算符所用的索引值不是无符号类型。

C 风格字符串

字符串字面值是 C++ 由 C 继承而来的 C 风格字符串。存放在字符数组中,以空字符 \0 结束。

C 标准 string 函数

现代的C++程序应当尽量使用 vector 和迭代器,避免使用内置数组和指针;应该尽量使用 string,避免使用C风格的基于数组的字符串。

3.6 多维数组

多维数组初始化

int ia[2][3] = {
{1, 2, 3},
{4, 5, 6}
}