第06章_函数
函数基础
局部对象
函数声明
- 定义函数的源文件应该把含有函数声明的头文件包含进来, 编译器负责验证函数的定义和声明是否匹配
分离式编译
参数传递
传值参数
传引用参数
const 形参和实参
- 当用实参初始化形参时会忽略掉顶层 const. 换句话说, 形参的顶层 const 被忽略掉了. 当形参有顶层 const 时, 传给它常量对象或者非常量对象都是可以的
- 由于顶层 const 被忽略了, 因此有无顶层 const 的形参对于函数重载是一样的
1
2void fcn(int i) {}
void fcn(const int i) {} // error, 重复定义
数组形参
main: 处理命令行选项
含有可变形参的函数
initializer_list 形参
- initializer_list 对象中的元素永远是常量值
1
2
3
4
5void error_msg(initializer_list<std::string> l) {
for (auto beg = l.begin(); beg != l.end(); ++beg) {
std::cout << *beg << std::endl;
}
}
省略符形参
- 省略符形参是为了便于 C++ 程序访问某些特殊的 C 代码而设置的
返回类型和 return 语句
无返回值函数
有返回值函数
- 返回一个值的方式和初始化一个变量或形参的方式完全一样: 返回的值用于初始化调用点的一个临时量, 该临时量就是函数调用的结果
- 不要返回局部对象的引用或指针
- 函数的返回类型决定函数调用是否是左值: 调用一个返回引用的函数得到左值, 其他返回类型得到右值
- 函数可以返回花括号包围的值的列表. 类似于其他返回结果, 此处的列表也用来对表示函数返回的临时量进行初始化.
尾置返回类型
1 | auto func(int i) -> int { return i; } |
使用 decltype
1 | int i = 0; |
返回数组指针
函数重载
- 同一作用域内的几个函数名字相同但形参列表不同, 称为函数重载
- main 函数不能重载
- 不允许两个函数除了返回类型外其他所有的要素都相同
- 一个拥有顶层 const 的形参无法和另一个没有顶层 const 的形参区分开来
1
2int func(int i);
int func(const int i); // error - 如果形参是某种类型的指针或引用, 则通过区分其指向的是常量对象还是非常量对象可以实现函数重载, 此时 const 是底层的
1
2
3
4
5int func(int *p);
int func(const int *p); // yes
int test(int &c);
int test(const int &c); // yes
重载与作用域
- 在内层作用域中声明函数, 将隐藏外层作用域中声明的同名实体
1
2
3
4
5
6
7
8
9void func(std::string s) {
std::cout << s << std::endl;
}
int main() {
void func(int a);
func("hello"); // error
return 0;
} - 当调用函数时, 编译器首先寻找对该函数的声明, 一旦在当前作用域中找到了所需的名字, 编译器就会忽略掉外层作用域中的同名实体
特殊用途语言特性
默认实参
- 通常, 应该在函数声明中指定默认实参, 并将该声明放在合适的头文件中
- 局部变量不能作为默认实参
- 只要表达式的类型能转换成形参所需的类型, 该表达式就能作为默认实参
- 用作默认实参的名字在函数声明所在的作用域内解析, 而这些名字的求值过程发生在函数调用时
内联函数和 constexpr 函数
- 内联说明只是向编译器发出的一个请求, 编译器可以选择忽略这个请求
- constexpr 函数是指能用于常量表达式的函数
- constexpr 函数体内也可以包含其他语句, 只要这些语句在运行时不执行任何操作就行
- 把内联函数和 constexpr 函数放在头文件中(可以在程序中多次定义, 因为编译器要想展开函数仅有函数声明是不够的, 还需要函数的定义. 但其多个定义必须完全一致, 因此通常定义在头文件中)
调试帮助
- assert 依赖一个名为 NDEBUG 的预处理变量的状态
函数匹配
实参类型转换
函数指针
- 函数指针指向的是函数而非对象
- 函数的类型由它的返回类型和形参类型共同决定, 与函数名无关
- 把函数名当作一个值使用时, 该函数自动地转换成指针
1
2pf = func;
pf = &func; // 等价 - 如果定义了指向重载函数的指针, 编译器通过指针类型决定选用哪个函数
- decltype 返回的是函数类型, 不会将韩式类型自动转换成指针类型, 所以只有加上 * 才能得到指针
1
2
3
4
5
6
7
8
9
10
11
12int func(int);
using Func = int(int);
typedef int Func(int);
typedef decltype(func) Func; // 与上两行等价, 都是函数类型
using FuncP = int(*)(int);
typedef int (*Func)(int);
typedef decltype(func) *FuncP; // 与上两行等价, 都是函数指针类型
void useFunc(Func);
void useFunc(FuncP); // 这两个声明语句声明的是同一个函数, 在第一个语句中, 编译器自动地将 Func 表示的函数类型转换成指针 - 返回指向函数类型的指针必须把返回类型写成指针形式, 编译器不会自动地将函数返回类型当成对应的指针类型处理