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

184 lines
8.4 KiB
Markdown
Raw Permalink 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: 6
---
# 函数
## 函数基础
- **函数定义**包括返回类型、函数名字和0个或者多个**形参**parameter组成的列表和函数体。
- **调用运算符**:调用运算符的形式是一对圆括号 `()`,作用于一个表达式,该表达式是函数或者指向函数的指针。
- 圆括号内是用逗号隔开的**实参**argument列表。
- 函数调用过程:
- 1.主调函数calling function的执行被中断。
- 2.被调函数called function开始执行。
- **形参和实参**:形参和实参的**个数**和**类型**必须匹配上。
- **返回类型** `void`表示函数不返回任何值。函数的返回类型不能是数组类型或者函数类型,但可以是指向数组或者函数的指针。
### 局部对象
- **名字**:名字的作用于是程序文本的一部分,名字在其中可见。
- **生命周期**:对象的生命周期是程序执行过程中该对象存在的一段时间。
- **局部变量**local variable形参和函数体内部定义的变量统称为局部变量。它对函数而言是局部的对函数外部而言是**隐藏**的。
- **自动对象**:只存在于块执行期间的对象。当块的执行结束后,它的值就变成**未定义**的了。
- **局部静态对象** `static`类型的局部变量,生命周期贯穿函数调用前后。
### 函数声明
- **函数声明**:函数的声明和定义唯一的区别是声明无需函数体,用一个分号替代。函数声明主要用于描述函数的接口,也称**函数原型**。
- **在头文件中进行函数声明**:建议变量在头文件中声明;在源文件中定义。
- **分离编译** `CC a.cc b.cc`直接编译生成可执行文件;`CC -c a.cc b.cc`编译生成对象代码`a.o b.o` `CC a.o b.o`编译生成可执行文件。
## 参数传递
- 形参初始化的机理和变量初始化一样。
- **引用传递**passed by reference又称传引用调用called by reference指**形参是引用类型**,引用形参是它对应的实参的别名。
- **值传递**passed by value又称传值调用called by value指实参的值是通过**拷贝**传递给形参。
### 传值参数
- 当初始化一个非引用类型的变量时,初始值被拷贝给变量。
- 函数对形参做的所有操作都不会影响实参。
- **指针形参**常用在C中`C++`建议使用引用类型的形参代替指针。
### 传引用参数
- 通过使用引用形参,允许函数改变一个或多个实参的值。
- 引用形参直接关联到绑定的对象,而非对象的副本。
- 使用引用形参可以用于**返回额外的信息**。
- 经常用引用形参来避免不必要的复制。
- `void swap(int &v1, int &v2)`
- 如果无需改变引用形参的值,最好将其声明为常量引用。
### const形参和实参
- 形参的顶层`const`被忽略。`void func(const int i);`调用时既可以传入`const int`也可以传入`int`
- 我们可以使用非常量初始化一个底层`const`对象,但是反过来不行。
- 在函数中,不能改变实参的**局部副本**。
- 尽量使用常量引用。
### 数组形参
- 当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
- 要注意数组的实际长度,不能越界。
### main处理命令行选项
- `int main(int argc, char *argv[]){...}`
- 第一个形参代表参数的个数第二个形参是参数C风格字符串数组。
### 可变形参
`initializer_list`提供的操作(`C++11`
| 操作 | 解释 |
|-----|-----|
| `initializer_list<T> lst;` | 默认初始化;`T`类型元素的空列表 |
| `initializer_list<T> lst{a,b,c...};` | `lst`的元素数量和初始值一样多;`lst`的元素是对应初始值的副本;列表中的元素是`const`。 |
| `lst2(lst)` | 拷贝或赋值一个`initializer_list`对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素。 |
| `lst2 = lst` | 同上 |
| `lst.size()` | 列表中的元素数量 |
| `lst.begin()` | 返回指向`lst`中首元素的指针 |
| `lst.end()` | 返回指向`lst`中微元素下一位置的指针 |
`initializer_list`使用demo
```cpp
void err_msg(ErrCode e, initializer_list<string> il){
cout << e.msg << endl;
for (auto bed = il.begin(); beg != il.end(); ++ beg)
cout << *beg << " ";
cout << endl;
}
err_msg(ErrCode(0), {"functionX", "okay});
```
- 所有实参类型相同,可以使用 `initializer_list`的标准库类型。
- 实参类型不同,可以使用`可变参数模板`
- 省略形参符: `...`,便于`C++`访问某些C代码这些C代码使用了 `varargs`的C标准功能。
## 返回类型和return语句
### 无返回值函数
没有返回值的 `return`语句只能用在返回类型是 `void`的函数中,返回 `void`的函数不要求非得有 `return`语句。
### 有返回值函数
- `return`语句的返回值的类型必须和函数的返回类型相同,或者能够**隐式地**转换成函数的返回类型。
- 值的返回:返回的值用于初始化调用点的一个**临时量**,该临时量就是函数调用的结果。
- **不要返回局部对象的引用或指针**。
- **引用返回左值**:函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值;其他返回类型得到右值。
- **列表初始化返回值**:函数可以返回花括号包围的值的列表。(`C++11`
- **主函数main的返回值**:如果结尾没有`return`编译器将隐式地插入一条返回0的`return`语句。返回0代表执行成功。
### 返回数组指针
- `Type (*function (parameter_list))[dimension]`
- 使用类型别名: `typedef int arrT[10];` 或者 `using arrT = int[10;]`,然后 `arrT* func() {...}`
- 使用 `decltype` `decltype(odd) *arrPtr(int i) {...}`
- **尾置返回类型** 在形参列表后面以一个`->`开始:`auto func(int i) -> int(*)[10]``C++11`
## 函数重载
- **重载**如果同一作用域内几个函数名字相同但形参列表不同我们称之为重载overload函数。
- `main`函数不能重载。
- **重载和const形参**
- 一个有顶层const的形参和没有它的函数无法区分。 `Record lookup(Phone* const)``Record lookup(Phone*)`无法区分。
- 相反是否有某个底层const形参可以区分。 `Record lookup(Account*)``Record lookup(const Account*)`可以区分。
- **重载和作用域**:若在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体,在不同的作用域中无法重载函数名。
## 特殊用途语言特性
### 默认实参
- `string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');`
- 一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。
### 内联inline函数
- 普通函数的缺点:调用函数比求解等价表达式要慢得多。
- `inline`函数可以避免函数调用的开销,可以让编译器在编译时**内联地展开**该函数。
- `inline`函数应该在头文件中定义。
### constexpr函数
- 指能用于常量表达式的函数。
- `constexpr int new_sz() {return 42;}`
- 函数的返回类型及所有形参类型都要是字面值类型。
- `constexpr`函数应该在头文件中定义。
### 调试帮助
- `assert`预处理宏preprocessor macro`assert(expr);`
开关调试状态:
`CC -D NDEBUG main.c`可以定义这个变量`NDEBUG`
```cpp
void print(){
#ifndef NDEBUG
cerr << __func__ << "..." << endl;
#endif
}
```
## 函数匹配
- 重载函数匹配的**三个步骤**1.候选函数2.可行函数3.寻找最佳匹配。
- **候选函数**选定本次调用对应的重载函数集集合中的函数称为候选函数candidate function
- **可行函数**考察本次调用提供的实参选出可以被这组实参调用的函数新选出的函数称为可行函数viable function
- **寻找最佳匹配**:基本思想:实参类型和形参类型越接近,它们匹配地越好。
## 函数指针
- **函数指针**:是指向函数的指针。
- `bool (*pf)(const string &, const string &);` 注:两端的括号不可少。
- **函数指针形参**
- 形参中使用函数定义或者函数指针定义效果一样。
- 使用类型别名或者`decltype`
- **返回指向函数的指针**1.类型别名2.尾置返回类型。