6 函数(重点)
重载函数,即同一个名字可以对应几个不同的函数。
6.1 函数基础
- 函数的调用:一是实参初始化函数对应的形参;二是将控制权转移给被调用函数。
- 函数的返回类型不能是数组类型或函数类型,但可以是指向数组或函数的指针。
- 在C++语言中,名字有作用域,对象有生命周期。把只存在于块执行期间的对象称为自动对象。
- 形参、局部变量和局部静态变量的区别?
P185
- 函数的名字必须在使用之前声明。函数的三要素(返回类型、函数名、形参类型)说明了调用该函数所需要的全部信息。建议变量和函数在头文件中声明,而在源文件中定义。含有函数声明的头文件应该被包含到定义函数的源文件中,编译器须验证函数的定义和声明是否匹配。
.cpp -> .o -> .exe <=> 源文件 -> 目标文件 -> 可执行文件
6.2 参数传递(重点)
- 传引用调用和传值调用。在C++语言中,建议使用引用类型的形参替代指针。如果函数无须改变引用形参的值,最好将其声明为常量引用。
- 与值传递相比,引用传递的优势主要体现在三个方面:一是可以直接操作引用形参所引的对象;二是使用引用形参可以避免拷贝大的类类型对象或容器类型对象;三是使用引用形参可以帮助我们从函数中返回多个值。理解对比普通变量、引用类型和常量引用作为函数参数的区别,
P191
。 - 在C++语言中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表应该有明显区别。
- C++允许我们用字面值初始化常量引用,但是不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参。
- 把函数不会改变的形参定义成普通引用将会有几个缺陷:一是容易给使用者一种误导,即程序允许变量的内容;二是限制了该函数所能接受的实参类型,使我们无法把const对象、字面值常量或者需要进行类型转换的对象传递给普通的引用形参。
- 数组的两个性质:一是不允许拷贝数组;二是使用数组时通常会将其转换成指针。管理数组实参的第一种方法是要求数组本身包含一个结束标志,典型示例是C风格字符串;第二种技术是传递指向数组首元素和尾后元素的指针;第三种方法是专门定义一个表示数组大小的形参。
- 只有当函数确实要改变元素值的时候,才把形参定义成指向非常量的指针。
int (&arr) [10]
// arr是具有10个整数的整型数组的引用 - *matrix 两端的括号必不可少:
int *matrix[10]; // 10个指针构成的数组
int (*matrix) [10]; // 指向含有10个整数的数组的指针 int main(int argc, char *argv[]) { ... }
或者int main(int argc, char **argv) { ... }
,其中第二个形参argv是一个数组,它的元素是指向C风格字符串的指针;第一个形参argc表示数组中字符串的数量。argv的第一个元素指向程序的名字或者一个空字符串,最后一个指针之后的元素值保证为0。- 省略符形参应该仅仅用于C和C++通用的类型。
6.3 返回类型和 return 语句
- return 语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。没有返回值的 return 语句只能在返回类型是 void 的函数中。强行令 void 函数返回其他类型的表达式将产生编译错误。
- return 语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换成函数的返回类型。在含有 return 语句的循环后面应该也有一条 return 语句,如果没有的话该程序就是错误的。
- 不要返回局部对象的引用或指针。调用运算符的优先级与点运算符和箭头运算符相同,并且也符合左结合律。调用一个返回引用的函数得到左值,其他返回类型得到右值。
- C++11新标准规定,函数可以返回花括号包围的值的列表。如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。
- 我们允许 main 函数没有 return 语句直接结束。返回 0 表示执行成功,返回其他值表示执行失败,其中非 0 值的具体含义依机器而定。
- 递归函数,即函数调用了它自身。main 函数不能调用它自己。
typedef int arrT[10];
// arrT 是一个类型别名,它表示的类型是含有10个整数的数组
using arrT[10];
// arrT 的等价声明
arrT* func(int i);
// func 返回一个指向含有10个整数的数组的指针
int (*func(int i)) [10];
// 声明一个函数返回一个指向含有10个整数的数组的指针
6.4 函数重载
- 重载函数:在同一个作用域内的几个函数名字相同但形参列表不同。函数重载可以在一定程度上减轻程序员起名字,记名字的负担。main 函数不能重载。不允许两个函数除了返回类型外其他所有的要素都相同。
- 一个拥有顶层 const 的形参无法和另一个没有顶层 const 的形参区分开来。如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的 const 是底层的。一般来说,是否重载函数要看哪个更容易理解。
- 函数匹配是指一个过程,在这个过程中我们把函数调用与一组重载函数中的某一个关联起来,函数匹配也叫做重载确定。一旦在当前作用域中找到了所需的名字,编译器就会忽略掉外层作用域中的同名实体。剩下的工作就是检查函数调用是否有效了。在 c++ 语言中,名字查找发生在类型检查之前。
6.5 特殊用途语言特性
- 默认实参,在函数的很多次调用中都被赋予一个相同的值。不过需要注意的是,一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参(靠右侧位置)。在给定的作用域中一个形参只能被赋予一次默认实参。
- 内联函数可避免函数调用的开销,内联机制用于优化规模较小、流程直接、频繁调用的函数。内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求。
- constexper 函数须要遵循的约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条 return 语句。把内联函数和 constexpr 函数放在头文件内。
- 头文件保护技术,以便有选择地执行调试代码。 assert 是一种预处理宏。
assert (expr );
// 首先对 expr 求值,如果表达式为假, assert 输出信息并终止程序的执行。 - assert 的行为依赖于一个名为 NDEBUG 的预处理变量的状态。如果定义了 NDEBUG ,则 assert 什么也不做。
6.6 函数匹配
- 函数匹配第一步,候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。
- 函数匹配第二步,可行函数也有两个特征:一是其形参数量与本次调用提供的实参数量相同,二是每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。
- 函数匹配第三步,从可行函数中选择与本次调用最匹配的函数。最佳匹配是指该函数每个实参的匹配都不劣于其他可行函数需要的匹配且至少有一个实参的匹配优于其他可行函数提供的匹配。调用重载函数时应尽量避免强制类型转换。
- 所有算术类型转换的级别都一样。调用形参为引用或指针类型情况下,如果两个函数的唯一区别是它的指针形参指向常量或非常量,则编译器能通过实参是否是常量决定选用哪个函数。
6.7 函数指针
- 函数指针指向的是函数而非对象。函数的类型由它的返回类型和形参类型共同决定。例:
bool (*bf) (const string&, const string& );
- 当我们把函数名作为一个值使用时,该函数自动地转换成指针。必须把返回类型写成指针形式,编译器不会自动地将函数返回类型当成对应的指针类型处理。 decltype作用于某个函数时,它返回函数类型而非指针函数。
待完善…