一、函数声明:
(1)函数声明由函数返回类型、函数名和形参列表组成,这三个元素被称为函数原型;
(2)如果函数定义和函数声明只有返回类型不一致,编译器会出错,属于同一声明;
(3)函数声明中的形参名会被忽略;
(4)函数一般在头文件中声明,然后在源文件中进行定义;
(5)如果声明两个函数void fun(inta)与void fun(const int a),会发生编译错误,认为是重复声明,因为这两个函数可以传递的实参类型完全一样;
二、默认实参:
(1)如果一个形参具有默认实参,那么后面的形参都必须有默认实参;
(2)如果某个形参传递时使用默认实参,那么它后面的形参也必须全部使用默认实参进行参数传递,所以使最少使用默认实参的形参排在最前面,最可能使用默认实参的形参排在最后;
(3)默认实参可以任何类型表达式,不要求是常量;
(4)既可以在函数声明中也可以在函数定义中使用默认实参,但是只能为形参指定默认实参一次,一般应该将默认实参放在函数声明中;
(5)类成员函数的常成员函数的const关键字必须在定义和声明全部都指定;inline函数的inline关键字在定义还是声明中指定都可以。
三、函数返回:
(1)函数必须指定返回类型,函数不能返回另一个函数或者内置数组类型,但是可以返回函数指针和指向数组元素的指针;
(2)返回类型不是void的函数必须返回(return)一个值,一个特例就是:容许主函数main没有return语句就结束,默认返回0。如果在if语句里进行函数返回,并不需要每个分支都有return返回,只要有return语句就可以通过编译;
(3)函数可以返回函数内局部对象,但是不能返回函数内局部对象的引用,也不能返回局部对象的指针;
(4)不要返回指向堆分配指针的引用,这样会造成内存泄漏,但可以返回局部动态申请的指针;
四、函数的参数传递:
(1)局部于函数的变量不能与函数的形参同名,它们属于同一个作用域;
(2)函数的形参不能用静态变量,涉及到堆栈调用;
(3)如果函数形参为const,既可以给该函数传递const实参也可以给该函数传递非const实参,如果函数形参是非const,同样既可以给该函数传递const实参也可以给该函数传递非const实参,所以编译器视const形参、非const形参(非指针、非引用)为同一声明,例如如果声明两个函数void fun(int a)与void fun(const int a),会发生编译错误,认为是重复声明;
(4)如果函数形参是指向const型对象的指针,则既可以给该函数传递const对象的地址 也可以给该函数传递非const对象的地址。如果该函数形参是指向非const对象的指针,则只能给该函数传递非const对象的地址。
(5)传递实参对象的局限性:
① 不能修改实参的值;
② 大型对象传递时很浪费空间;
③ 有些类只声明不定义的情况下,无法用实参传递给函数;
(6)非const类型的引用形参不能传递右值实参和可以默认转换的类型的实参,只能传递同类型的非const类型的实参。const类型的引用形参既可以传递右值实参和默认转换类型的以及非const类型的实参,所以尽量将引用形参定义为const引用;
(7)形参可以声明为数组的引用,如果是数组的引用,编译器不会将数组转换为指针,传递给数组的引用本身,这个时候编译器将要检查数组大小是否匹配,声明数组引用的形参如下: void fun((&arr)[10]),这样数组只能传递固定大小的数组实参,可以通过使用参数重载,实现任意大小的数组传递:
Template<size_t N> void fun(int (&arr)[N]);---参数重载
五、其它注意点:
(1)函数名查找发生在类型检测之前,所以如果局部声明的函数名字与调用的名字相同, 编译器则不去再寻找其他函数声明了;
(2)如果在调用函数的时候,给实参做了操作,会改变实参的值,如下面:
fun(s++);s的值是++以后的值,注意和值传递的区别!
(3)在函数中,尽可能延迟变量的定义出现时间,因为如果程序出现奔溃或者提早跳出程序,那么提前定义变量就会浪费构造和析构成本,在定义的同时尽量直接赋值初始化,也会减少变量默认构造的成本;
(4)一般在循环里定义变量比在循环外定义变量好,这样容易维护和理解。