常规函数调用是使程序跳到地址,并在函数结束时返回。执行函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈,跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入到寄存器中),然后跳回到地址被保存的指令处。 显然来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。
内联函数是C++为提高程序运行速度所做的一项改进。编译器使用相应的代码替换函数调用。对于内联函数程序不需跳到另一个位置处执行代码,再跳回来。因此内联函数的运行速度比常规函数稍快。
宏定义与内联函数:
使用宏定义定义函数有以下缺陷:
#define SQUARE(X) X*X
a = SQUARE(4+4); //被替换为a = 4+4*4+4,很明显结果并不是期望的
int x = 3;
x = SQUARE(x++); //结果为3*4 而不是3*3
因此应考虑将其转换为C++内联函数。
引用更接近指针常量,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它。
int rats = 101;
int &rodents = rats;
//上述代码是下列代码的伪装表示
int * const pr = &rats;
在进行函数传参时,如果引用参数是const,则编译器将在下面两种情况下生成临时变量:
1、实参的类型正确,但不是左值;2、实参类型不正确,但可以转换为正确的类型。
左值分为const变量和常规变量,const变量是不可修改的左值,常规变量是可修改的左值。
右值引用:
右值往往是没有名称的,因此要使用它只能借助引用的方式。这就产生一个问题,实际开发中我们可能需要对右值进行修改(实现移动语义时就需要),显然左值引用的方式是行不通的。为此,C++11 标准新引入了另一种引用方式,称为右值引用,用 "&&" 表示。
int num = 10;
//int && a = num; //右值引用不能初始化为左值
int && a = 10;
模板并不创建任何函数,而只是高速编译器如何定义函数。需要交换int的函数时,编译器将按模板模式创建这样的函数,并用int代替AnyType。
template //也可以使用typename
void Swap(AnyType& a,AnyType& b){
AnyType temp = a;
a = b;
b = temp;
}
当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板。
//explicit specialization显示具体化
template<> void Swap(job &, job &); //method 1
template<> void Swap(int &, int &); //method 2
在C++中函数调用优先级为:常规函数>显示具体化>常规模板。
隐式实例化(implicit instantiation)函数调用Swap(i,j)直接导致编译器生成Swap()的一个实例,该实例使用int类型。
显示实例化(explicit instantiation)直接命令编译器创建特定的实例。语法如下:
template void Swap(int &, int &); //method 1
Swap(a,b); //method 2
可以看出显示具体化在template后加了<>,而显示实例化没有加<>。显示具体化是声明另一个函数模板,用来处理特别的数据类型。而显示具体化是通过类型命令编译器创建函数定义。
int x;
decltype(x) y; //使用x的类型来声明y
decltype(expression) var为确定类型,编译器必须遍历一个核对表,核对表的简化版如下:
第一步:如果expression是一个没有用括号括起来的标识符,则var的类型与该标识符的类型相同。如上代码所示。
第二步:如果expression是一个函数调用,则var的类型与函数的返回类型相同。
第三步:如果(expression)是一个左值,则var为指向其类型的引用.
第四步:如果前面的条件都不满足,则var类型与expression的类型相同。
C++允许使用以下这种后置返回类型来声明函数的返回类型,auto是一个占位符,表示后置返回类型提供的类型。
template
auto gt(T1 x,T2 y) -> decltype(x+y){
return x+y;
}