概述
问题一:关于内联函数(inline function)的种种细节
首先,为什么要引入内联函数?在提出内联函数之前,如果一个函数体中调用了其他的函数(比如A调用了B),那么编译器在执行到A中的B时,会暂停A的执行,寄存器保存当前的执行地址,并产生一块内存区域,用来保存B的返回值。然后去寻找B函数,执行完B函数将结果保存在刚才产生的内存区域中。然后找到寄存器的执行地址,继续执行A的代码。这样来回跳跃并记录跳跃位置,需要一定的开销。
因此提出了内联函数,在函数的声明处对其定义。这样编译器在编译时遇到该函数,并不会跳出来去寻找该函数,而是直接将该函数的代码嵌入到当前函数中,减少了开销。不过带来的副作用就是内存的开销,会占用更多的内存。因此在使用内联函数时一定要确保所使用的内联函数时比较小的。(貌似如果内联函数比较大,会默认的将内联函数替换成常规函数)。
另外,内联函数不能执行递归。(why?)
提到内联函 数,通常会和宏定义放在一起比较。宏定义只是常规的文本替换,和内联函数不同。并且使用宏定义的时候要非常的小心,对变量和函数体要加括号。比如:
#define square(x) x*x;
square(4+5)并不会返回81,而是4+ 5 * 4 + 5等于29
问题二:关于引用(&)
引用是已定义的变量的别名。主要用在函数的形参中,这样函数将使用原始数据,而不是副本。好处是减少开销,可以像指针一样对原始数据进行修改。
切记一点:引用必须在声明时对其初始化。
如果一个函数的返回值是引用类型,会更高效一点,因为不会产生一个临时变量用来存储函数的返回值。直接返回的必须是一个引用变量(即函数的某个引用形参)。
当然也可以在函数中new一块区域存储变量,使返回值返回这个变量。但是这样做很容易忘了delete这块new的区域。
另外如果不需要改变引用变量的值,可以使用const来修饰引用类型的形参。
问题三:关于函数中的默认参数
函数的实参赋值顺序是从左到右,函数的默认参数赋值则是从右到左。
函数的默认参数设置必须通过函数原型。
问题四:关于函数重载和函数模板
在函数重载中,将类型引用和类型本身视为同一种类型。例如:
double function(double x) 与 double function(double & x)相同
函数模板使用泛型来定义函数。可以使用如下两种方式建立模板:
template <typename T> template<class T>
模板中有一个概念“显示具体化”(explicit specialization)。在原版中是这样解释的:If the complier find a specialized definition that exactly matches a function's call,it ues the definition without looking for templates.也就是说如果存在显示具体化的原型,就不会再去寻找函数模板了。显示具体化优先于模板。
那么,在这么多的函数重载,函数模板,函数模板重载中,编译器是如何选择合适的函数版本去执行需要的操作呢?
基本流程如下:
1. 创建候选函数列表(选择函数名称相同的)
2. 使用候选列表创建可行函数列表(选择参数数目相同,参数类型可以隐式的转换成相同类型的)
3. 确定是否有最佳的选择,如果有,调用执行,否则报错。
因此提出了一个最佳选择的概念,如何去衡量最佳?
1. 可以完全匹配。这种情况下如果既存在常规函数又存在函数模板,选择常规函数(参数为非const类型的函数优先于const修饰的函数)
2. 提升转换(如char可以隐式的转换成int)
3. 标准转换(如int转换为char)
4. 用户自定义转换(函数定义在使用之前,将忽略其他的函数模板)
C++11中提供了decltype(declare type)用来解决下面的问题:
template <class T1,class T2>
void fun(T1 a,T2 b)
{
x = a + b;
}
x为什么类型的?用decltype(a+b)x,编译器会明确x 的类型为a+b的类型。
同时C++11还提供了auto fun(T1 a,T2 b)->decltype(a+b)来明确函数的返回类型。
最后
以上就是殷勤凉面为你收集整理的C++ Primer Plus学习之函数篇二的全部内容,希望文章能够帮你解决C++ Primer Plus学习之函数篇二所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复