6.1 函数基础
1. 实参和形参的区别
- 形参:在函数参数列表中声明的局部变量。 它们由每次调用函数时提供的实参初始化。
- 实参:在函数调用中提供的值,用于初始化函数的形参。
2. 局部对象
(1)局部静态对象
局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,在函数结束时还存在,直到程序终止才被销毁。
- 局部静态对象只被初始化一次。
(2) 函数应该在头文件中声明,在源文件中定义
3.分离式编译
6.2 参数传递
1. 使用引用避免拷贝
string 对象可能会非常长,所以应该尽量避免直接拷贝它们,这时使用引用形参是比较明智的选择。
1 | // compare the length of two strings |
- 尽量使用常量引用。
- 如果函数无需改变引用形参的值,最好将其声明为常量引用。
2. const 形参和实参
1 | void fcn(const int i) { /* fcn 能读取 i,但是不能向 i 写值 */} |
调用 fcn 函数时,既可以传入 const int,也可以传入 int。
3. 数组引用形参
形参也可以是数组的引用
1 | // ok: parameter is a reference to an array; the dimension is part of the type |
(&arr)
两端的括号不可少- 这一用法无形中限制了只能将函数作用于大小为 10 的数组
1 | int k[10] = {0,1,2,3,4,5,6,7,8,9}; |
4. 传递多维数组
1 | // matrix 指向数组的首元素,该数组的元素是又 10 个整数构成的数组 |
5. main 处理命令行选项
1 | int main(int argc, int *argv[]) |
- 第二个形参 argv是一个数组,它的元素是指向 C 风格字符串的指针。
- 第一个形参 argc 表示数组中字符串的数量。
argv[0] = “prog”; // or argv[0] might point to an empty string
argv[1] = “-d”;
argv[2] = “-o”;
argv[3] = “ofile”;
argv[4] = “data0”;
argv[5] = 0;
当使用 argv 中的实参时,可选的实参要从 argv[1] 开始,argv[0]保存程序的名字,而非用户输入。
6. initializer_list 形参
initializer_list 是一种标准库类型,用于表示某种特定类型的值的数组。
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:
1 | void err_msg(ErrCode e, initializer_list<string> il){ |
initializer_list
对象中的元素永远是常量值。- 所有实参类型相同,可以使用
initializer_list
的标准库类型。 - 实参类型不同,可以使用
可变参数模板
。 - 省略形参符:
...
,便于C++
访问某些 C 代码,这些 C 代码使用了varargs
的 C 标准功能。
6.3 返回类型和 return 语句
1. 无返回值函数
没有返回值的 return
语句只能用在返回类型是 void
的函数中,返回 void
的函数不要求非得有 return
语句。
2.有返回值函数
return
语句的返回值的类型必须和函数的返回类型相同,或者能够隐式地转换成函数的返回类型。- 值是如何返回的:返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果。
- 不要返回局部对象的引用或指针。
- 引用返回左值:函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值;其他返回类型得到右值。
1 | char &get_val(string &str, string::size_type ix) |
- 列表初始化返回值:函数可以返回花括号包围的值的列表。(
C++11
) - 主函数main的返回值:如果结尾没有
return
,编译器将隐式地插入一条返回0的return
语句。返回0代表执行成功。
3. 返回数组指针
Type (*function (parameter_list))[dimension]
1
2
3
4
5
6int (*func(int i))[10];
- func(int i)表示调用 func 函数时需要一个 int 类型的实参。
- (*func(int i)) 意味着我们可以对函数调用的结果执行解引用的操作
- (*func(int i))[10] 表示解引用 func 的调用将得到一个大小是 10 的数组
- int (*func(int i))[10] 表示数组中的元素是 int 类型。使用类型别名:
1 | typedef int arrT[10]; // arrT 是一个类型别名,它表示的类型是含有 10 个整数的数组。 |
- 使用
decltype
:decltype(odd) *arrPtr(int i) {...}
- 尾置返回类型:
1 | 在形参列表后面以一个`->`开始: |
6.4 函数重载
- 重载:如果同一作用域内几个函数名字相同但形参列表不同,我们称之为重载(overload)函数。
必须要形参数量或者形参类型不同,不能仅仅返回类型不同。
main
函数不能重载。- 重载和const形参:
- 一个有顶层const的形参和没有它的函数无法区分。
Record lookup(Phone* const)
和Record lookup(Phone*)
无法区分。 - 相反,是否有某个底层const形参可以区分。
Record lookup(Account*)
和Record lookup(const Account*)
可以区分。
- 一个有顶层const的形参和没有它的函数无法区分。
- 重载和作用域:若在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体,在不同的作用域中无法重载函数名。
6.5 特殊用途语言特性
默认实参
string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');
- 一旦某个形参被赋予了默认值,那么它后面的形参都必须要有默认值。
- 函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参。
内联(inline)函数
- 普通函数的缺点:调用函数比求解等价表达式要慢得多。
inline
函数可以避免函数调用的开销,可以让编译器在编译时内联地展开该函数。- 适用于规模小,流程直接,频繁调用的函数。
inline
函数应该在头文件中定义。
constexpr函数
- 指能用于常量表达式的函数。
constexpr int new_sz() {return 42;}
- 函数的返回类型及所有形参类型都要是字面值类型。
- 只有一个 return 语句。
constexpr
函数应该在头文件中定义。
调试帮助
assert
是一种预处理宏(preprocessor macro)assert(expr);
expr 为真时,assert 输出信息并终止程序执行。- 预处理名字由预处理器而非编译器管理。
开关调试状态:
CC -D NDEBUG main.c
可以定义这个变量NDEBUG
。
1 | void print(){ |
6.6 函数匹配
- 重载函数匹配的三个步骤:1.候选函数;2.可行函数;3.寻找最佳匹配。
- 候选函数:选定本次调用对应的重载函数集,集合中的函数称为候选函数(candidate function)。
- 可行函数:考察本次调用提供的实参,选出可以被这组实参调用的函数,新选出的函数称为可行函数(viable function)。
- 寻找最佳匹配:基本思想:实参类型和形参类型越接近,它们匹配地越好。
6.7 函数指针
函数指针:是指向函数的指针。函数的类型由它的返回类型和形参类型共同决定。
1 | bool lengthCompare(const string &, const string &); |
- 函数指针形参:
- 形参中使用函数定义或者函数指针定义效果一样。
- 使用类型别名或者
decltype
。
1 | // third parameter is a function type and is automatically treated as a pointer to function |
直接使用函数指针冗长而繁琐:
1 | // Func and Func2 have function type |
- 返回指向函数的指针:
- 1.类型别名;
using F = int(int*, int); // F is a function type, not a pointer
using PF = int(*)(int*, int); // PF is a pointer type
- 2.尾置返回类型。
- decltype 作用于某个函数时,它返回函数类型而不是指针,需要显式地加上
*
- decltype 作用于某个函数时,它返回函数类型而不是指针,需要显式地加上
- 1.类型别名;