默认实参

某些函数包含一些实参,在函数的很多次调用中他们都被赋予相同的一个值,此时我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函数时,我们可以包含该实参,也可以省略该实参。


(资料图片仅供参考)

例如我们用string对象表示窗口内容,一般我们希望该窗口的高、宽和背景字符都是用默认值,但是同时我们也允许用户为这几个参数自由指定与默认值不同的数值。我们把他定义成以下形式:

我们为每一个参数都提供了默认实参,默认实参作为形参的初始值出现在形参列表中,我们可以为一个或多个形参定义默认值,一旦某个形参被赋予了默认值,他后面的所有形参都必须有默认值。

使用默认实参调用函数

如果我们想使用默认实参,只要在调用函数的时候省略该实参就行了。

函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参

第二种传递的虽然是个?字符,但是会自动转换为sz的无符号整数,也就是ascii码的值。

当设计含有默认实参的函数时,一般将不常用的形参写在前面,而将常用的写在后面。

默认实参声明

在给定作用域中一个形参只能被赋予一次默认形参,也就是函数后续的声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参都必须又默认值。

内联函数和constexpr函数

调用函数一般比求等价表达式的值要慢一些。在大多数机器上,一次函数调用其实包含这一系列工作,调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参,程序转向一个新的位置继续执行。

内联函数可避免函数调用的开销

将函数指定为内联函数,通常是将他在每个调用点上“内联的”展开。如果我们把之前的shorterString函数定义成内联函数

将在编译过程中展开成类似

的形式,这样就消除了shorterString函数运行时的开销。

在shorterString函数的返回类型前加上关键字inline,这样就可以将它声明为内联函数了。

一般来说,内联机制用于优化规模小、流程直接、频繁调用的函数。很多编译器都不支持内联递归函数,而且一个75行的函数也不太可能在调用点内敛地展开。

constexpr函数

constexpr函数是指能用于常量表达式的函数。定义constexpr函数的方法和其他函数类似,不过需要注意,函数的返回类型及所有形参的类型都得是字面值类型,而且函数中必须有且只有一条return语句。

执行该初始化任务时,编译器把对constexpr函数的调用替换成其结果值,为了能在编译过程中随时展开,constexpr函数被隐式的指定为内联函数。

constexpr函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行,

如果我们用一个非常量表达式调用scale函数,则返回值是一个非常量表达式。

constexpr函数不一定返回常量表达式。

由于内联函数和constexpr函数可以在程序中多次定义,编译器要想展开函数仅有函数声明是不够的,还要函数的定义。不过对于某个给定的内联函数或者constexpr函数来说,他的多个定义必须完全一致,所以一般将内联函数和constexpr函数定义在头文件中。

调试帮助

有时我们会用到一种类似于头文件保护的技术,以便有选择地执行调试代码。基本思想是,程序可以包含一些用于调试的代码,但是这些代码只在开发程序时使用,当应用程序编写完成时,要先屏蔽调试代码,这种方法用到两项预处理功能,assert和NDEBUG。

assert预处理宏

assert是一种预处理宏,所谓预处理宏其实是一个预处理变量,他的行为有点类似于内联函数,assert宏使用一个表达式作为他的条件,

首先对expr求值,如果表达式为假,assert输出信息并终止程序的执行,如果表达式为真,assert什么也不做。

assert宏定义在cassert头文件中。

预处理名字由预处理器而不是编译器管理,因此我们可以直接使用预处理名字而无需提供using声明。我们应该直接使用assert而不是std::assert,也不需要using。

和预处理变量一样,宏名字在程序内必须唯一,含有assert头文件的程序不能再定义名为assert的变量、函数或者其他实体。所以无论如何我们应该避免使用assert作为名字。

assert宏常用于检查“不能发生的条件”。例如,一个对输入文本进行操作的程序可能要求所有给定单词的长度都大于某个阈值,此时我们可以使用

NDEBUG预处理变量

assert的行为依赖于一个名为NDEBUG的预处理变量的状态,如果定义了NDEBUG则assert什么也不做,默认状态下没有定义NDEBUG,此时assert将执行运行时检查。

我们可以使用#define语句定义NDEBUG,从而关闭调试状态。所以,assert应该仅用于验证那些确实不可能发生的事情,我们可以把assert当成调试程序的一种辅助手段,但是不能用它替代真正的运行时逻辑状态,也不能替代程序本身应该包含的错误检查。

除了用于assert外,NDEBUG也可以编写自己的条件调试代码。如果NDEBUG未定义,将执行#ifndef和#endif之间的代码,如果定义了NDEBUG,这些代码被忽略

我们使用变量__func__输出当前调试的函数的名字,编译器为每个函数都定义了__func__,他是const char的一个静态数组,用于存放函数的名字。

还有四个对于程序调试很有用的名字

如果我们给程序提供了一个长度小于threshold的string对象,将得到下面的错误消息。

推荐内容