C++primer学习笔记-----6.5特殊用途语言特性

来源:互联网 发布:星巴克巧克力知乎 编辑:程序博客网 时间:2024/05/17 02:28

6.5.1 默认实参


【某些函数有这样一些实参:在函数的大多数调用情况下它们都被赋予相同的值,但在某些少数特定情况下,它们也需要被赋予其它的值
我们可以把这样特点的函数输入定义为函数的默认实参
调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。】

【含有默认实参的函数定义代码如下:
typedef string::size_type sz; // 和默认实参无关,只是本例用到了(类型别名)string screen(sz ht = 24; sz wid = 80; char backgrnd = ' '); // 在形参后提供一个等号紧跟着该形参的默认实参
我们可以为一个或多个形参定义默认值,不过需要注意的是,一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。这是为了令非默认实参与形参列表的匹配方式只有一种,否则实参与形参的匹配方式就无法唯一确定了。】

【如果我们想使用默认实参,只要在调用函数的时候省略该实参就可以了。
函数调用时实参按其位置解析,默认实参负责调补函数调用中缺少的尾部实参,即提供的实参从左往右依次匹配形参列表,不允许跳过,剩余得不到匹配的形参则与默认实参匹配。同样是为了使实参与形参的匹配方式具有唯一确定性。
string window;window = screen(, , '?'); // 错误:只能省略尾部的实参window = screen('?'); // 调用screen('?', 80, ' ');符号也是整形,问号会将其机械值作为无符号整数传递给第一个sz 类型的形参

【当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序,尽量让不怎么使用默认值的形参出现在前面,而让那些经常使用默认值的形参出现在后面
如果不这么设计,那么会经常出现这样一种情形,即因为希望使用非默认实参的参数位置靠后,在其之前本想使用默认实参的参数也不得不作为非默认实参再书写一遍。】

【对于函数,多次声明是合法的。
对于具有默认实参的函数来说,在给定的作用域中一个形参只能被赋予一次默认实参。函数的后续声明只能为之前那些没有默认实参值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。(即在声明中可以逐步添加默认实参,但必须从右向左不允许跳过)
string screen(sz, sz, char = ' ');string screen(sz, sz, char = '*'); // 错误:重复声明string screen(sz = 24, sz = 80, char); // 正确:添加默认实参

【局部变量不能作为默认实参。(负责函数将在该变量的作用域外调用时将会出错)
sz wd = 80;char def = ' ';sz ht(); // 这三个变量的声明必须出现在函数外,作为全局变量存在string screen(sz = ht(), sz = wd, char = def);
用作默认实参的名字在函数声明所在的作用域内解析(完成名字与对象匹配),而这些名字的求值过程发生在函数调用时(对象与其值匹配)
void f2(){    def = '*'; // 改变了外部对象def 的值    sz wd = 100; // 隐藏了外层定义的wd,但外部对象wd 的值未改变    window = screen(); // 调用screen(ht(), 80, '*'),screen 在声明时已经绑定了外部变量wd,虽又定义了局部变量wd 隐藏了外部wd 的名字,但绑定关系没有改变}

6.5.2 内联函数和constexpr 函数


【内联函数可避免函数调用时的开销:
将函数指定为内联函数,可以使编译器在对其编译时尝试将它在每个调用点上“内联地”展开,而非在实际运行时调用。(是一种请求,并非一定会被执行)
在函数的返回类型前面加上关键字inline ,就可以将它声明为内联函数了。
通常,内联机制用于优化规模较小、流程直接、频繁调用的函数。有一部分编译器不支持内联递归函数。】

【constexpr 函数:
constexpr 函数是指能用于常量表达式的函数。这种函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return 语句
constexpr int new_sz() {return 42;}constexpr int foo = new_sz();
执行该初始化任务时,编译器把对constexpr 函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr 函数被隐式地指定为内联函数(能够在编译时就推断出其返回值,因而规模一定要小)。
constexpr 函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。(空语句、类型别名、using 声明)】

【constexpr 函数不一定返回常量表达式
constexpr size_t scale(size_t cnt) { return new_sz() * cnt;} // scale(arg) 的类型与arg 相同(是否为常量表达式)
constexpr 这一机制的目的就是让编译器来替我们检查一个值是否是常量表达式,并尝试性地将该值用到需要常量表达式的地方。(正是有了检查这一过程,才使尝试性地应用有了实现的可能)
内联函数和constexpr 函数通常定义在头文件中。】

6.5.3 调试帮助


【程序可以包含一些用于调试的代码,但是这些代码只在开发程序时使用。当应用程序编写完成准备发布时,要先屏蔽掉调试代码。
这种方法用到两种功能:assert 和NDEBUG。】

assert 预处理宏
assert 是一种预处理宏。所谓预处理宏其实是一个预处理变量,它的行为有点类似于内联函数。assert 用一个表达式作为它的条件。
assert(expr)
首先对expr 求值,如果表达式为假,assert 输出信息并终止程序的运行;如果表达式为真,assert 什么都不做
assert 宏常常用于检查“只能为真”的条件(程序的逻辑只允许出现的状况)。】

NDEBUG 预处理变量
assert 的行为依赖于一个名为NDEBUG 的预处理变量的状态。如果定义了NDEBUG,assert 什么都不做,默认状态下没有定义NDEBUG。】

【除了与assert 联合使用,也可以使用NDEBUG 编写自己的条件调试代码:
如果NDEBUG 未定义,将执行#ifndef 和#endif 之间的代码,定义NDEBUG 可以屏蔽这两条之间的代码
#ifndef NDEBUGcerr << _ _func_ _ << ":a mistake here";
#endif
C++ 编译器定义了_ _func_ _ 这个静态数组存放函数的名字;预处理器也定义了四个名字:_ _FILE_ _, _ _LINE_ _, _ _TIME_ _, _ _DATE_ _。
这些名字对于调试函数很有帮助,能够帮助我们定位异常出现的位置。】
原创粉丝点击