doc/docs/Cpp-Notes-main/Cpp-Primer/3.字符串、向量和数组.md
sairate 4108f6c42d docs(book): 添加现代 C++教程及相关代码
- 新增现代 C++ 教程的 Preface 章节,包括英文和中文版本
- 添加 C++ Primer 练习代码
- 新增 Learn C++ 教程的 C++ 开发简介章节
- 添加头文件解析文档
- 更新 mkdocs.yml,包含新教程的目录结构
- 修改项目设置,使用 Python 3.10环境
2025-07-06 16:14:43 +08:00

243 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
sort: 3
---
# 字符串、向量和数组
## 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`
`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`对象)的下标运算符,只能对确知已存在的元素执行下标操作,不能用于添加元素。
- 两个`vector`对象相等当且仅当他们所含的元素个数相同,而且对应位置的元素也相同。
## 迭代器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表达式或者不写让编译器自己推断。
- 定义数组必须指定数组的类型,不允许用`auto`关键字由初始值的列表推断类型,但在遍历时允许使用`auto`
- 不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
- 不允许使用一个数组为另一个内置类型的数组赋值,也不允许使用`vector`对象初始化数组,相反地,允许使用数组来初始化`vector`对象。
- 不存在引用的数组。
- 理解数组声明的含义,最好的板房是从数组的名字开始按照由内向外的顺序阅读。
### 访问数组元素
- 数组下标的类型:`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和迭代器少用内置数组和指针应尽量使用string避免使用C风格的基于数组的字符串**
## 多维数组
- **多维数组的初始化**
- 使用范围for语句时除了最内层的循环外其他所有循环的控制变量都应该是**引用**类型。
## 指针vs引用
- 引用总是指向某个对象,定义引用时没有初始化是错的。
- 给引用赋值,修改的是该引用所关联的对象的值,而不是让引用和另一个对象相关联。
## 指向指针的指针
- 定义: `int **ppi = &pi;`
- 解引用:`**ppi`
## 动态数组
- 使用 `new``delete`表达和c中`malloc``free`类似的功能,即在堆(自由存储区)中分配存储空间。
- 定义: `int *pia = new int[10];` 10可以被一个变量替代。
- 释放: `delete [] pia;`,注意不要忘记`[]`