函数是一个命名了的代码块,可以重载函数,也就是同一个名字对应不同的函数
6.1函数基础
返回类型 、函数名 、 0个或者多个形参列表 、函数体
我们通过调用运算符来执行函数,函数调用完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调用函数;执行函数第一步是定义并初始化形参;遇到一条return语句时结束函数;return语句也完成两项工作:一是返回return语句中的值,二是控制权交给主调函数
- 形参和实参
实参类型必须与对应形参匹配,实参与形参存在对应但是并没有规定实参求值顺序
是否施舍未命名的形参并不影响调用时提供的实参数量,即使形参不被函数使用,也必须为他提供一个实参;函数返回类型不能是数组、函数类型,但是可以是指向数组、函数的指针
6.1.1局部对象
c++中名字有作用域、对象有生命周期
**局部对象:**形参和函数体内部定义的变量统称局部变量,仅在函数作用域中可见,局部变量会隐藏外层作用域中同名的其他所有声明
自动对象对于普通的局部变量对应的对象来说,当函数的控制路径经过该变量是创建该对象,到该对象定义的块语句结束时销毁它,把这种只存在于块执行期间的对象称为自动对象;普通局部变量应该定义时初始化
也就是说,定义于函数体内的内置类型对象如果定义时没有初始化,则其值没有定义。类对象没有初始化,则其值由类决定。而对于局部静态对象来说,虽然定义于函数体内,但是函数结束执行也对他没有影响,直到程序结束才被销毁,如果定义时没有初始化,则默认初始化为0;
函数声明:与函数定义相比,只需用分号代替函数体;
函数应该在头文件中声明在源文件中定义,
含有函数声明的文件应该被包含到定义函数的源文件中
6.1.3分离式编译:允许把文件分割到几个文件中去,每个文件单独编译;
编译和连接多个源文件
6.2参数传递:
形参类型决定了实参和形参交互的方式。如果形参是引用类型它将绑定到对应实参上,否则将实参的值拷贝后赋给形参。
传值参数:
传值参数的机理与变量赋值类似:
int n=0;//int类型初识量
int i=n;//i是n的值的副本
i=42;//i的值改变,不影响n的值
指针形参:指针形参与其他非引用类型一样。当执行指针拷贝操作时,拷贝的是指针的值。拷贝之后两个指针是不同的指针,因为指针使我们可以间接地访问她所指向的对象,所以指针可以修改它所指向的对象的值,但实参本身没有改变。访问函数外部对象时,建议使用应用类型代替指针
6.2.2传递引用参数、
- 使用引用避免拷贝
- 使用引用返回额外信息
6.2.3const形参和实参
形参的初始化方式与变量是一样的;
顶层const作用于对象本身
实参初始化形参时忽略掉顶层const,当形参有顶层const时,传递给她的实参常量或者非常量对象都可以
指针或引用形参与const:
可以使用非常量初始化一个底层const对象,但是反过来不可以
尽量使用常量引用:
6.2.4数组形参:
两个性质:不允许拷贝数组、使用数组时会将其转换成指针
所以当为函数传递一个数组时,实际上传递的是指向数组首元素的指针
三个函数完全等价:
void print(const int *);
void print(const int[]);//
void print(const int[10]);//我们期望数组含有多少元素,实际不一定
管理指针形参三个技术:(因为数组是传递的指针,一开始函数不知道数组尺寸,调用者应该为此提供一些额外信息。
第一种:
使用表及指定数组长度(如c风格字符串)
第二种:
使用标准库规范;传递指向数组首元素和尾后元素的指针;
比如begin(arr)返回数组首元素指针,end(arr)返回数组的尾后指针
第三种
显示传递一个表示数组大小的形参
比如传递end(arr)-begin(arr);
数组引用形参:void print(int (&arr)[10]);//但是函数只能使用长度为10的数组
6.2.5main:处理命令行选项
6.2.6含有可变形参的函数
c++11提供了两种方法:如果所有实参类型相同可以使用initializer_list的标准库类型;
如果实参类型不同可以使用可变参数模板;
省略符:一般使用于与c函数交互的程序接口
initialiizer_list形参:实参数量未知,类型全部相同便可以使用
**与vector相似之处都是模板类型,不同之处在于,他的元素永远都是常量,
6.3返回类型与return语句
return语句终止当前函数并将控制权返回给调用该函数的地方
无返回值的函数:
void函数最后一个return可以不显示给出
有返回值的函数:
只要函数类型不是void,则每条返回语句必须返回一个值,返回值的类型要与函数的返回类型相同,或者能隐式转换成函数返回类型;
在含有return语句的循环后面应该也有一条return语句,很多编译器无法发现此类错误
- 值是如何被返回的
返回一个值的方式与初始化一个变量或形参的方式完全相同:返回的值用于初始化调用点的一个临时量,该临时变量就是函数调用的结果
不要返回局部对象的引用和指针
**调用一个返回引用的函数得到左值,其他返回类型得到右值
decltype(*P)l类型是一个引用,对p指向的类型的引用
decltype(())也是引用;
列表初始化返回值
类似其他返回结果此处的列表用来对表示函数返回的临时量赋值;
主函数main的返回值
允许main函数没有返回语句
预处理变量:EXIT_FAILURE
EXIT_SUCCESS;
6.3.3返回数组指针
typedef int arr[10];
等价于
using arr=int[10];
声明一个返回数组指针的函数:
type (*function(parameter_list))[dimension]; 如果没有外面的括号函数的返回类型将是指针的数组
int (*func(int i))[10];
简化方法-尾置返回类型:
例如:
auto func(int *)->int( * )[10];
使用decltype
6.4函数重载
编译器根据实参类型推断使用的是哪个函数
重载和const形参:顶层const不影响传入函数的对象,一个拥有顶层const的形参无法与一个没有顶层const的形参区分开来;
**但是另一方面:如果形参是某种类型的指针和引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层const
允许常量引用绑定非常量的对像,字面值甚至表达式
只能在const对象上执行不改变其内容的操作,如初始化等
对于底层const 可以使用重载,编译器通过是否是常量推断应该调用哪个函数
因为const不能转换成其他类型,所以是const实参必须对应const版本函数,而非const变量可以转换成const类型
调用重载的函数:函数匹配或者说重载确定
调用重载函数的三个结果:
最佳匹配;无匹配;二义性调用;
6.4.1重载与作用域
c++中名字查找发生在类型检查之前
6.5特殊用途语言特性
6.5.1默认实参
设计默认实参时,其中一项任务是合理设置形参顺序,尽量让不怎么使用默认实参的形参位于前面
6.5.2内联函数与constexpr函数
6.5.3调试帮助
assert和NDEBUG
6.6函数匹配
第一步确定本次调用对应的重载函数集(函数名、声明点可见)
第二步考察提供的实参,然后选出能被这组实参调用的函数,可行函数:两个特征形参数量与实参相等,二是每个实参类型与形参相同或者能转换成形参的类型,
第三步寻找最佳匹配
6.6.1实参类型转换
6.7函数指针:指向函数而非对象,和其他指针一样,函数指针指向某种特定类型,由返回类型与参数类型决定
声明一个指向函数的指针只需要用指针替换函数名即可
函数指针形参:和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针