Android C++ 系列:C++11 函数特殊特性
1. 背景
在 Python 中函数有默认参数等,在 C++11 中我们发现 C++也支持了默认参数;还有 C++特有的内联函数、constexpr 函数等知识都有不少细节,本文对这些知识做详细介绍。
2. 默认参数
我们在 Java 中重载函数时经常有这样的形式:
同一个函数因为参数个数不同最后定义了好几种形式,有些场景我们可以这样理解,参数较少的函数只是参数较多的函数的某个参数是个特定值,比如上面第二个构造函数可以理解成第一个构造函数的 defStyleAttr 为 0,我们不去使用这个参数。这样就面临一个问题,明明一个函数可以解决的事情,最后搞出好几个函数来,有没有一种简便方法呢?在 Python 中我们用了默认参数实现,C++11 也提供了这种特性。
某些函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参(default argument)。调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
实际的代码工程中经常看到这种(随便从工程代码截取一段):
再举个例子说明:
我们设置坐标值,如果是二维空间 z 坐标都为 0,这时候我们只需要这样调用:
如果是三维需要设置 z 坐标:
我们可以定义一个或者多个形参的默认值,但是要注意:
一旦一个形参被赋予了默认值,它后面的所有形参都必须有默认值,换句话说就是默认参数必须放在参数列表的最后面。
调用函数时,其中一个参数使用了默认值,这个参数对应形参里面后面的所有参数都必须使用默认值。
**最佳实现:**设计含有默认实参的函数时,尽量让不怎么用到默认值的形参出现在前面,让经常使用默认值的形参出现在后面。
对于函数的声明来说,我们的习惯是将其放在头文件中,并且一个函数只声明一次,但是 C++中多次声明同一个函数也是合法的。我们在给定的作用域中一个形参只能被赋予一次默认实参。意味着,函数的后续声明只能为之前那些没有默认值的形参添加默认实参,而且该形参右侧的所有形参必须都有默认值。
通常我们使用的默认实参都是字面值,其实只要表达式的类型能转换成形参所需的类型,这个表达式就能作为默认实参。注意:局部变量不能作为默认实参。
用作默认实参的名字在函数声明所在的作用域内解析,而这些名字的求值过程发生在函数调用时:
我们在 fun 内部改变了 dz 值,所以调用 setPosition 时传递的是更新过的值。
3. 内联函数
我们一般的函数调用会有以下动作:
调用前先保存寄存器;
在返回时恢复;
如果需要会拷贝实参
程序转向一个新的位置继续执行。
总结下来就是函数调用是有不小开销的。有没有办法优化这块呢?答案是内联函数。
内联函数实际就是在每个调用点上”内联的“展开。
比如我们定义了一个内联函数:
调用函数:
再编译过程会展开成下面形式:
上面也看到了,内联函数的定义是在函数返回类型前面加上关键字 inline。
内联关键字是想编译器发出请求,编译器具体实现时也可以忽略这个请求。
内联机制用于优化规模较小,流程直接,频繁调用的函数。对于大部分编译器,不支持内联递归函数,对于 75 行的函数也大概不允许在调用节点内联展开。
4. constexpr 函数
constexpr 函数是指能用于常量表达式的函数。定义 constexpr 函数的方法与其他函数类似,不过要遵循以下几项规定:
函数的返回类型及所有形参的类型都得是字面值类型
函数体中必须有且只有一条 return 语句。
执行初始化任务时,编译器把对 constexpr 函数的调用替换成其结果值。和 inline 一样都是在编译时,所以为了能在编译过程中随时展开,constexpr 函数被隐式地指定为内联函数。
constexpr 函数体内也可以包含其他语句,只要这些语句在运行时不执行任何操作就行。例如,constexpr 函数中可以有空语句、类型别名以及 using 声明。
最佳实践:
内联函数和 constexpr 函数可以在程序中多次定义。因为编译器要想展开函数仅有函数声明是不够的,还需要函数的定义。但是对于某个给定的内联函数或者 constexpr 函数来说,它的多个定义必须完全一致。基于这个原因,内联函数和 constexpr 函数通常定义在头文件中。
5. 总结
本文介绍了 C++函数的特殊语法:默认参数、内联函数、constexpr 函数。其中默认参数是 C++11 新特性,constexpr 默认也是内联的,内联是在调用节点展开,有点类似宏展开。
版权声明: 本文为 InfoQ 作者【轻口味】的原创文章。
原文链接:【http://xie.infoq.cn/article/f92143fcb49116f2b0225eb67】。文章转载请联系作者。
评论