6-5 特殊用途语言特性
作者:互联网
6.5.1 默认实参
某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参(default argument)。调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
例如,我们使用string对象表示窗口的内容。一般情况下,我们希望该窗口的高、宽和背景字符都使用默认值。但是同时我们也应该允许用户为这几个参数自由指定与默认值不同的数值。为了使得窗口函数既能接纳默认值,也能接受用户指定的值,我们把它定义成如下的形式:
typedef string::size_type sz;
string screen(sz ht = 24; sz wid = 80; char backgrnd = ' ');
注意:一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值
使用默认实参调用函数
string window;
window = screen(); //screen(24,80,' ')
window = screen(66); //screen(66,80,' ')
window = screen(66,256); //screen(66,256,' ')
window = screen(66,256,'#'); //screen(66,256,'#')
函数调用时实参按其位置解析,默认实参负责填补函数调用缺少的尾部实参(靠右侧位置)。例如,要想覆盖backgrnd 的默认值,必须为ht和 wid提供实参:
window = screen(, ,'?'); //错误:只能省略尾部实参
window = screen('?'); //调用screen('?', 80, '?')
需要注意,第二个调用传递一个字符值,是合法的调用。然而尽管如此,它的实际效果却与书写的意图不符。该调用之所以合法是因为'?'是个 char,而函数最左侧形参的类型string::size_type是一种无符号整数类型,所以char类型可以转换成(参见4.11节,第141页)函数最左侧形参的类型。当该调用发生时,char类型的实参隐式地转换成string::size_type,然后作为 height的值传递给函数。在我们的机器上,'?'对应的十六进制数是0x3F,也就是十进制数的63,所以该调用把值63传给了形参height。
当设计含有默认实参的函数时,其中一项任务是合理设置形参的顺序。尽量让不怎么使用默认值的形参出现在前面,而让那些经常使用默认值的形参出现在后面。
默认实参声明
对于函数的声明来说,通常的习惯是将其放在头文件中,并且一个函数只声明一次,但是多次声明同一个函数也是合法的。不过有一点需要注意,在给定的作用域中一个形参只能被赋予一次默认实参。换句话说,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。假如给定
//表示高度和宽度的实参没有默认值
string screen(sz , sz , char = ' ');
//我们不能修改一个已经存在的默认值
string screen(sz , sz , char = '*'); //错误:重复声明
//但是可以按照如下方式添加默认实参
string screen(sz = 24, sz = 80, char ); //正确:添加默认实参
通常,应该在函数声明中指定默认实参,并将该声明放在合适的头文件中。
默认实参初始值
局部变量不能作为默认实参。除此之外,只要表达式的类型能转换成形参所需的类型,该表达式就能作为默认实参:
//wd、def和ht的声明必须出现在函数外
sz wd = 80;
char def = ' ';
sz ht();
string screen(sz = ht(), sz = wd, char = def);
string window = screen(); //调用screen(ht(), 80, ' ')
用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时:
void f2(){
def = '*'; //改变默认实参的值
sz wd = 100; //隐藏了外层定义的wd,但是没有改变默认值
window = screen(); //调用screen(ht(), 80, '*');
}
我们在函数f2内部改变了def的值,所以对screen的调用将会传递这个更新过的值。另一方面,虽然我们的函数还声明了一个局部变量用于隐藏外层的 wd,但是该局部变量与传递给screen的默认实参没有任何关系。
6.5.2 内联函数和constexpr函数
内联函数
在6.3.2节(第201页)中我们编写了一个小函数,它的功能是比较两个string形参的长度并返回长度较小的 string 的引用。把这种规模较小的操作定义成函数有很多好处,主要包括:
-
阅读和理解shorterstring 函数的调用要比读懂等价的条件表达式容易得多。
-
使用函数可以确保行为的统一,每次相关操作都能保证按照同样的方式进行。
-
如果我们需要修改计算过程,显然修改函数要比先找到等价表达式所有出现的地方再逐一修改更容易。
-
函数可以被其他应用重复利用,省去了程序员重新编写的代价。
然而,使用shorterstring函数也存在一个潜在的缺点:调用函数一般比求等价表达式的值要慢一些。在大多数机器上,一次函数调用其实包含着一系列工作:调用前要先保存寄存器,并在返回时恢复;可能需要拷贝实参;程序转向一个新的位置继续执行。
内联函数可避免函数调用的开销
将函数指定为内联函数(inline),通常就是将它在每个调用点上“内联地”展开。假设我们把shorterstring函数定义成内联函数,则如下调用
cout<<shorterString(s1,s2)<<endl;
在编译时,内联函数的内容会直接拷贝到调用位置上,上述内联函数在编译时展开为:
cout<< (s1.size() < s2.size() ? s1 : s2) <<endl;
这样,既保证了可读性与复用性,又消除了直接调用函数shortString()函数的开销
使用内联函数
在shorterstring 函数的返回类型前面加上关键字inline,这样就可以将它声明成内联函数了:
//内联版本:寻找两个string对象中较短的那个
inline const string &
shortString(const string &s1, const string &s2){
return s1.size() < s2.size() ? s1 : s2;
}
一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。同时,很多编译器都不支持内联递归函数,而且一个75行的函数也不大可能在调用点内联地展开。
constexpr函数
标签:函数,screen,特性,默认,用途,内联,实参,string,语言 来源: https://www.cnblogs.com/timothy020/p/15914809.html