Table of Contents generated with DocToc
与操作符相比,函数也会通过一系列的计算,产生结果,但是函数有自己的名字,操作数没有数量限制,两者都可以重载。
函数体就是一个作用域。
函数的形参为函数提供了已命名的局部存储空间。
实参必须具有与形参类型相同、或能隐式转换为形参类型的数据类型。
函数不能返回另一函数或者内置数组类型,但可以返回函数的指针,或指向数组元素的指针的指针。
没有显式指定返回类型的函数定义或声明是不合法的。
形参可为空,但不能省略。
如果两个参数具有相同的类型,其类型必须重复声明,不允许这样的声明void fun(int a, b);
形参的初始化与变量一样,如果形参具有非引用类型,则复制实参的值;如果为引用类型,则它只是实参的别名。
函数的形参可以是指针,此时将复制实参指针。该类形参的任何改变也仅仅作用于局部副本。如果函数将新指针赋给形参,主调函数使用的实参指针的值没有任何改变。
尽管函数的形参是const,但编译器却将其视为形参被声明为普通的类型。
void fcn(const int i){}
void fcn(int i){}// ERROR, Redefine function
不适宜复制实参的情况包括:
- 当需要在函数中修改实参的值时。
- 需要以大型对象作为实参传递时。
- 没有办法实现对象的复制时。
解决办法就是将形参定义为引用或指针。
如果使用引用形参的唯一目的是避免复制实参,那就利用const引用。
非const引用实参只能和完全同类型的非const对象关联。
int incr(int&val) {return ++val;}
int main()
{
short v1 = 0;
const int v2 = 32;
int v3 = incr(v1); //ERROR
v3 = incr(v2); //ERROR
v3 = incr(0); //ERROR
v3 = incr(v1 + v2); //ERROR
int v4 = incr(v3); //OK
}
C++倾向于传递指向容器中需要处理的元素的迭代器来传递容器。
将数组形参直接定义为指针要比使用数组语法定义更好,这就明确表明了函数操纵的是指向数组元素的指针。
编译器忽略任何数组形参指定的长度,不会去检测数组的长度。
void pintArray(int[])
与 void pintArray(int[10])
等同
通过引用传递数组:
void printArray(int (&arr)[]){}
int *arr[10]; //array of 10 points
int (*arr)[10]; //pointer to an array of 10 elements
怎样确保函数的操作不超出数组实参的边界:
- 在数组本身存放一个标志位
- 传递指向数组第一个和最后一个元素的下一个位置的指针
- 将第二个形参定义为数组的大小
含有可变形参的函数
void foo(parm_list, …);
void foo(…);
返回类型为void
的函数中,return
不是必须,有隐藏的return
在最后一条语句完成时。
返回非引用:函数的返回值用于在初始化在调用函数处创建的临时对象。
返回引用:返回的是对象本身。
千万不要返回局部对象的引用或指针!!!
函数可以返回引用,作为一个左值。
主函数main不能调用自身。
函数与变量类似的是:先声明后使用,定义一次,声明多次。
默认实参只能替换函数调用缺少的尾部实参,因为函数调用时,实参是从左向右赋给形参的。
一个文件中,只能为一个形参指定默认实参一次。
如果函数定义的形参表中提供默认实参,则只有在包含该函数定义的源文件中调用该函数时,默认实参才有效。
自动对象:至于当定义它的函数被调用时才存在的对象,比如形参。
静态局部对象:位于函数作用域内,生命期跨越了这个函数的多次调用。
避免函数调用的开销。
内联只是对编译器的一个建议,编译器可以忽略!
把内联函数放入头文件,这样修改要重新编译所有的源文件。
函数原型必须在类中定义,函数体可以在类外。
编译器隐式地将类内定义的成员函数当作内联函数。
成员函数含有额外的、隐含的形参,即类对象本身this。
任何程序只有一个main函数,main函数不可重载!
每一个版本的重载函数都应该在同一作用域中声明。
重载确定的三个步骤:
1) 候选函数,同名
2) 选择可行函数,形参与实参个数相同,每一个实参的类型与对应的形参匹配
3) 寻找最佳匹配
4) 含有多个形参的重载确定
a) 每个实参的匹配都不劣于其它
b) 至少一个实参的匹配优于其它
允许将形参定义为函数类型,但函数的返回类型必须是指向函数的指针,而不是函数。
上一章:[5. 表达式](5. 表达式.md)
下一章:8. 标准IO库