基础部分
1,实参是函数形参的初始值,函数的每次调用都要经历形参的初始化过程(每次调用函数都会重新创建它的形参),并且实参的数量必须与形参相匹配或者实参的类型能转换成形参的类型。
2,函数的返回类型不能使数组和函数,但是可以是函数的指针和指向数组的指针,返回类型可以是void。
3,形参(也定义与函数体内部)和函数体内部定义的变量称为局部变量,同时局部变量还会隐藏在外层作用域中的同名的其他所有声明,局部变量的声明周期依赖于定义的方式,如果要让局部对象在函数调用结束以后还能存在,并且维持到直到程序结束,可以将局部变量定义成static类型(static如果没有显示的初始化,它将执行值初始化,内置类型的局部静态变量初始化为零)
4,函数必须在使用之前进行声明,函数声明三要素(函数名,返回类型,形参列表),函数声明最好包含在头文件中。
5,如果形参是引用类型(引用),它将绑定到对应的实参上,否则将实参的值拷贝后赋给形参。注意,指针形参(当执行指针拷贝操作的时候,拷贝的是指针的值)
**6,尽量使用引用,避免拷贝,因为大的类型拷贝比较消耗资源,而且当函数无需修改引用形参的值得时候,最好将引用形参声明为常量应用。技巧:可以使用引用形参返回额外的信息
比较深入部分
1,实参初始化形参时,会忽略顶成const(也就是说如果形参是一个常量,那么实参可以是常量,也可以是非常量)。
2,我们可以用一个非常量来初始化一个底层const对象(也就是说可以用一个非常量来初始化一个指向常量的指针或者引用,但是不可以用一个常量来初始化一个普通的指针或者普通的引用),而且普通的引用(或者指针)必须使用同类型的对象来初始化。
3,数组形参
1,数组有两个性质,不允许拷贝数组以及在使用数组时(通常)会将其转换成指针,所以当我们为函数传递一个数组的时候,实际上传递的是指向数组首元素的指针。和一下三个列子一样:
void print(const int*);
void print(const int[]);
void print(const int[10]);
注意,使用数组时必须防止下标越界,因为这将产生未定义的行为。
管理数组越界的方法
(1)数组本身包含标记,比如c风格的字符串的尾部为/0。
(2)传递指向数组首元素和尾后元素的指针(可以使用begin和end函数)
(3)传递一个指示数组大小的形参。
2,关于引用的讨论适用于指针。
3,关于二维数组的传递
一下两个传递方式是等价的
void print(int (*mat)[10]);
void print(int mat[][10]);
最好使用第一种的传递多维数组的方式
4,含有可变参数
如果函数实参的数量未知,但是实参类型的全部相同,我们可以使用标准库类型(initializer_list,其定义在同名的头文件中)的形参,其定义形式如下:
void error_msg(int a,initializer_list<string> il);
还有一种省略符形参,注意省略符形参只能放在形参列表的最后一个。
5,不要返回局部对象的指针或者引用。
6,调用运算符也有优先级和结合律,调用运算符的优先级和点运算符,箭头运算符(箭头运算符只能指针使用)相同,且都满足左结合律
7,返回数组指针
这么声明一个返回数组指针的函数呢
int (*fun(int i))[10];
auto func(int i)->int(*)[10];
decltype(odd) *func(int i);
8,函数重载
定义:如果在同一个作用域内的几个函数名字相同,但形参列表不同,我们称之为重载函数。
1,我们不允许两个函数除了返回类型外其他所有的要素都相同。
2,一个拥有顶成const的形参无法和另一个没有顶层const的形参区分开来,当然底层const是可以区分的。