第七章 函数
第1节 函数基础
栈帧结构
函数的外部链接
第2节 函数详解
传值、传址、传引用
传参数时的类型退化,传数组时函数形参退化成指针,所以形参不要写数组个数
多维数组作为函数参数时
void fun(int (*par)[4])
int a[3][4] = {0};
fun(a);
避免退化的写法
void fun(int (&par)[3])
int a[3];
fun(a);
变长参数
-
initializer_list
,变量类型必须相同#include <initializer_list> void fun(std::initializer_list<int> par) fun({1,2,3,4,5});
-
可变长度模板参数 (不限制类型相同,复杂一些)
-
使用省略号表示形参(不建议使用)
函数缺省实参
void fun (int x = 0){} fun();
缺省实参右侧的参数都必须有缺省实参
传入实参时从左到右与形参匹配
在一个翻译单元中,每个参数的缺省实参只能定义一次
缺省实参为对象时,实参的缺省值会随对象的值变化而变化 (不建议,阅读困难)
推荐方法:源文件中定义函数,头文件中声明函数同时定义缺省实参,main函数中调用函数,最安全。
main函数的两个版本
- 无形参
int main(){}
- 带两个形参的
int main(int argc,char* argv[]){}
argc
为非负数,程序运行环境传递给程序的实参个数
argv
为指针,指针指向argc+1
个指针的数组
函数体
函数体形成域
- 其中包含自动对象
- 可包含局部静态对象
函数体的返回值
- 隐式返回 void
- 显式返回
return
返回值优化(RVO)
返回类型
表示函数计算结果类型,可以为void
返回类型的几种书写方式
-
经典:位于函数头前部
-
11引入新方法:位于函数头的后部
auto fun(int a,int b) -> int { return a + b; }
-
14引入方式:返回类型自动推导
17引入返回类型的结构化绑定
第3节 函数重载与重载解析
(重载的函数,参数类型不相同)
int fun(int x){
return x+1;
}
double fun(double x){
return x+1;
}
fun(3);
fun(3.5);
使用相同的函数名定义多个函数,每个函数具有不同的参数列表
- 不能基于不同的返回类型进行重载
- 函数重载与mangling
编译器如何选择正确的版本完成调用
名称查找
限定查找(限定查找域)与非限定查找
非限定查找会进行逐级域查找——名称隐藏(同一域的函数在域内会隐藏其他域的同名函数)
查找通常会在已声明集合中进行
实参依赖查找
namespace MyNS{
struct Str{};
void g(Str s){
}
}
MyNS::Str obj;
g(obj);
重载解析
在名称查找基础上进一步选择合适的调用函数
-
过滤不能被调用的版本
- 参数个数不对
- 实参无法转换为形参
- 实参不满足形参的限制条件
-
在剩余版本中查找最匹配的版本,匹配级别越低越好(同一匹配级别视为重定义)
-
完美匹配,平凡转换(加const)
-
promotion或加平凡转换
-
标准转换或标准平凡转换
-
*自定义转换
-
*调用形参为省略号的版本
-
-
函数包含多个形参时,所选函数的所有形参的匹配级别都要优于或等于其他函数
第4节 函数相关的其他内容
递归函数
在函数体内调用其自身的函数
通常用于描述复杂的迭代过程(注意避免无线递归)
内联函数
调用函数时,函数体运算较简单,则较多资源用于构建和释放栈帧,针对这一问题编译期可以展开函数,避免构建栈帧。
inline
内联标识符,向编译器发出请求,将函数编译时展开
inline
内联标识符,将程序级的一次定义原则改为翻译单元级别的一次定义原则
向编译器发出请求,将函数编译时展开
constexpr函数与consteval函数
为了让**某些函数在编译期计算**,引入constexpr函数
(内联函数和constexpr函数都应该定义在头文件中,翻译单元级不重复)
constexpr int fun(int x){
return x + 1;
}
constexpr int y = 3;
int z = fun(y);
实参也需要是字面值或常量编译表达式
函数指针
函数类型与函数指针类型
int fun(int x){}
函数类型为int(int)
using K = int(int);
K fun; //相当于函数声明,不可用于定义
或者配合std::function
使用
(函数指针多数跟高阶函数配合使用)
函数指针与重载
函数指针作为参数
using K = int(int);
void pla(K* input) {
std::cout << (*input)(2) << std::endl;
}
int inc(int x) {
return x + 1;
}
int main(int argc, char *argv[]) {
pla(inc);
}
函数指针作为返回值
int inc(int x){
return x+1;
}
int dec(int x){
renturn x-1;
}
auto fun(bool input){
if(input)
return inc; //返回函数指针
else
return dec;
}
std::cout<<(*fun(true))(100)<<std::endl;