(6)函数

来源:互联网 发布:通州淘宝城在哪个位置 编辑:程序博客网 时间:2024/06/08 10:13
  1. 内置类型的未初始化局部变量将产生未定义的值。
  2. 某些时候, 有必要令局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成static类型从而获得这样的对象。 局部静态对象在程序的执行路径第一次经过对象定义语句时初始化, 并且直到程序终止才被销毁, 在此期间即使对象所在的函数结束执行也不会对它有影响。如果局部静态变量没有显式的初始值, 它将执行值初始化, 内置类型的局部静态变量初始化为0(185)。
  3. 如果一个函数永远也不会被我们用到,那么它可以只有声明没有定义。函数声明也称作函数原型。回忆之前所学的知识, 我们建议变量在头文件中声明,在源文件中定义。与之类似,函数也应该在头文件中声明而在源文件中定义。如果把函数声明放在头文件中,就能确保同一函数的所有声明保持一致。而且一旦我们想改变函数的接口, 只需改变一条声明即可。含有函数声明的头文件应该被包含到定义函数的源文件中(186)。
  4. 当执行指针拷贝操作时, 拷贝的是指针的值。拷贝之后, 两个指针是不同的指针。(188)
  5. 当函数无须修改引用形参的值时最好使用常量引用。
  6. 和其他初始化过程一样,当用实参初始化形参时会忽略掉顶层const。换句话说,形参的顶层const被忽略掉了。 当形参有顶层const时,传给它常量对象或者非常量对象都是可以的。忽略掉形参的顶层const 可能产生意想不到的结果:
    void fcn(const int i) { /* fcn能够读取i ,但是不能向i写值 */}
    void fen (int i) {/**/}//错误: 重复定义了fcn(int)
    在C++语言中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表应该有明显的区别。 因为顶层const被忽略掉了,所以在上面的代码中传入两个fcn函 数的参数可以完全一样。 因此第二个 fcn 是错误的, 尽管形式上有差异,但实际上它的 形参和第一个fcn的形参没什么不同(191)。
  7. 可以使用非常量初始化一个底层const对象, 但是反过来不行; 一个普通的引用必须用同类型的对象初始化。(191)
  8. C++允许我们用字面值初始化常量引用。我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用形参(192)。
  9. 数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响,这两个性质分别是:不允许拷贝数组以及使用数组时(通常)会将其转换成指针。因为不能拷贝数组,所以我们无法以值传递的方式使用数组参数。因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
    void print(const int*);
    void print(const int[]);
    void print(const int[10]); // 三个是等价的
    和其他使用数组的代码一样,以数组作为形参的函数也必须确保使用数组时不会越界(193)。
  10. 管理数组实参的三种技术:
    ①要求数组本身包含一个结束标记,使用这种方法的典型示例是C风格字符串(这种方法适用于那些有明显结束标记且该标记不会与普通数据混淆的情况,但是对于像 int这样所有取值都是合法值的数据就不太有效了。)
    ②传递指向数组首元素和尾后元素的指针。
    ③专门定义一个表示数组大小的形参,在C程序和过去的C++程序中常常使用这种方法(194)。
  11. 当函数不需要对数组元素执行写操作的时候,数组形参应该是指向const 的指针。只有当函数确实要改变元素值的时候,才把形参定义成指向非常量的指针。数组引用形参(195)
  12. 传递多维数组(196)。
  13. 为了编写能处理不同数量实参的函数,C++11新标准提供了两种主要的方法:如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板。C++还有一种特殊的形参类型(即省略符〉,可以用它传递可变数量的实参。需要注意的是,这种功能一般只用于与C函数交互的接口程序。initializer_list形参:如果函数的实参数量未知但是全部实参的类型都相同,我们可以使用initializer_list类型的形参。和vector不一样的是, initializer _list对象中的元素永远是常量值 , 我们无法改变initializer_list对象中元素的值。如果想向 initializer _list形参中传递一个值的序列,则必须把序列放在一对花括号内。省略符形参:省略符形参是为了便于C++程序切问某些特殊的C代码而设置的,这些代码使用了名为varargs的C标准库功能(199)。
  14. 没有返回值的return语句只能用在运回类型是void的函数中。返回void的函数不要求非得有return语句, 因为在这类函数的最后一句后面会隐式地执行return。一个返回类型是void的函数也能使用return语句的第二种形式,不过此时return 语句的 expression必须是另一个返回 void的函数。强行令void函数返回其他类型的表达式将产生编译错误。return语句返回值的类型必须与函数的返回类型相同, 或者能隐式地转换成函数的返回类型。尽管C++无法确保结果的正确性, 但是可以保证每个return语句的结果类型正确。 也许无法顾及所有情况,但是编译器仍然尽量确保具有返回值的函数只能通过一条有效的return 语句退出(200)。
    1. 在含有return语句的循环后面应该也有一条return语句,如果没有的话该程序就是错误的。很多编译器都无法发现此类错误。不要返回局部对象的引用或指针。 要想想确保返回值安全,我们不妨提问:引用所引的是在函数之前已经存在的哪个对象?(201)
    2. 调用运算符的优先级与点运算符和箭头运算符,并且也符合向左结合律。函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值(202)。
    3. C++11新标准规定,函数可以返回花括号包围的值的列表。类似于其他返回结果,此处的列表也用来对表示函数返回的临时量进行初始化。如果列表为空,临时量执行值初始化,否则返回的值由函数的返回类型决定。如果函数返回的是内置类型,则花括号包围的列表最多包含一个值,而且该值所占空间不应该大于目标类型的空间。如果函数返回的是类类型,有类本身定义初始值如何使用。主函数main的返回值:为了使返回值与机器无关,cstdlib头文件定义了两个预处理变量,我们可以使用这两个变量分别表示成功与失败:EXIT_FAILURE和EXIT_SUCCESS(203)。
    4. 因为数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或引用。如果我们想定义一个返回数组指针的函数,则数组的维度必须跟在函数名字之后,然而,函数的形参列表也跟在函数名字后面且形参列表应该先于数组的维度。 因此,返回数组指针的函数形式如下所示(205):
      type (*fimction (parameter_list)) [dimension]
    5. 使用尾置返回类型:
      // func接受一个int类型的实参 , 返回一个指针, 该指针指向含有10个整数的数组
      auto func(int i) -> int(*) [10);
      还有一种情况,如果我们知道函数返回的指针将指向哪个数组, 就可以使用 decltype关键字声明返回类型(206)。
    6. main函数不能重载。不允许两个函数除了返回类型外其他所有的要素都相同。顶层const不影响传入函数的对象。一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。另一方面,如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载,此时的const是底层的(208)。
    7. 其实,重载对作用域的一般性质并没有什么改变:如果我们在内层作用域中声明名字,它将隐藏外层作用域中声明的同名实体(210)。
    8. 一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么使用默认值的形参出现在前面,而让那些经常使用默认值的形参出现在后面。在给定的作用域中-个形参只能被赋予一次默认实参。换句话说, 函数的后续声明只能为之前那些没有默认值的形参添加默认实参, 而且该形参右侧的所有形参必须都有默认值。 局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型, 该表达式就能作为默认实参。用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时(注意)(213)。
    9. constexpr函数是指能用于常量表达式的函数。 定义constexpr函数的方法与其他函数类似, 不过要遵循几项约定: 函数的返回
      类型及所有形参的类型都得是字面值类型, 而且函数体中必须有且只有一条return语句。执行该初始化任务时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开, constexpr函数被隐式地指定为内联函数。constexpr函数体内也可以包含其他语句, 只要这些语句在运行时不执行任何操作就行。 例如, constexpr函数中可以有空语句、 类型别名以及using声明。constexpr函数不一定返回常量表达式(214)。
    10. 和其他他函数不一样,内联函数和constexpr函数可以在程序中多次定义。毕竟,编译器要想展开函数仅有函数声明是不够的,还需要函数的定义。不过,对于某个给定的内联函数或者constexpr函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和constexpr函数通常定义在头文件中(215)。
    11. assert是一种预处理宏。它的行为有点类似于内联函数。assert宏使用一个表达式作为它的条件: assert (expr) ; 首先对expr求值,如果表达式为假(即0), assert输出信息并终止程序的执行。 如果表达式为真(即非 0), assert什么也不做。应该使用assert而不是 std: :assert, 也不需要为assert提供using声明。和预处理变量一样,宏名字在程序内必须唯一。含有 cassert头文件的程序不能再定义名为assert的变量、函数或者其他实体。在实际编程过程中,即使我们没有包含assert头文件,也最好不要为了其他目的使用assert,很多头文件都包含了cassert,这就意味着即使你没有直接包含cassert,它也很有可能通过其他途径包含 在你的程序中。assert宏常用于检查“不能发生”的条件(215)。
0 0
原创粉丝点击