函数
函数是一个可以重复使用的代码块,CPU在执行主函数代码时如果遇到了被调函数,主函数就会被暂停,CPU转而去执行被调函数中的代码,当被调函数执行完毕后再返回到主函数,主函数根据刚才的状态继续往下执行,直到结束。
内联函数
通过“内存膨胀”的方式,以空间(计算机内存的代码区)换取时间,提高程序的运行速度。即内联函数不需要进行入栈和出栈,从而达到解决时间的目的。
内联函数的语法:
//inline语法
/*
inline 返回值类型 函数名(参数表){
函数体;
}
*/
1、为什么要引入内联函数
函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。如果函数体代码比较多,则需要较长的执行时间,那么函数调用机制(入栈、出栈)占用的时间可以忽略;如果函数体代码较少时,那么大部分的时间都会花费在函数调用机制上,时间花销较大。为了消除函数调用的时间开销,C++提出在编译时将函数调用处用函数体替换,类似于c语言中的宏展开。
引入内联函数的主要目的是,用它代替C语言中表达式形式的宏定义来解决程序中函数调用的效率问题。
在c语言中可以使用如下的宏定义来解决函数调用的效率问题
#difine ExpressionName(x,y) ((x) + (y)) * ((x) - (y))
这种宏定义的方式有一下优缺点:
优点:
这种宏定义在形式上及使用上像一个函数,但它使用预处理器实现,没有了参数压栈、代码生成等一系列操作,因此效率提高。
缺点:
①该宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做了预处理器符号表中的简单替换,因此他不能进行参数的有效性检测,同时它返回值也不能被强制转换为可专函的合适类型,这样,他的使用就存在一系列的隐患和局限性。
②在c++中引入了类及类的访问控制,如果一个操作或者说一个表达式涉及类的保护成员或私有成员,就不可以使用这种宏定义来实现(因为无法将this指针放在合适位置)。
inline函数的目的就是取代这种表达式形式的宏定义。
上述宏定义可以改写成如下所示的内联函数:
#include<iostream>
using namespace std;
inline int func(int &x, int &y) {
int tmp = 0;
tmp = (x + y) * (x - y);
return tmp;
}
int main() {
int a, b, c;
a = 1;
b = 2;
cout << "a=" << a << ",b=" << b << endl;
c = func(a, b);
cout << "c=" << c << endl;
return 0;
}
程序运算输出结果如下所示:
2、为什么内联函数能取代表达式形式的宏定义
①inline定义的类的内联函数,函数的代码被放入符号表中,在使用的时候直接进行替换,没有子调用的开销,提高效率
②类的内联函数也是一个真正的函数。编译器在调用一个内联函数时,首先会检查他的参数类型,保证调用参数的有效性,然后进行一系列相关检查,就像对待任何一个真正的函数一样,消除了使用宏定义时的隐患和局限性
③inline可以作为某个类的成员函数,就可以使用所在类的保护乘员及私有成员
3、内联函数的缺点
不能将所有的函数都定义成内联函数,因为内联函数是以代码膨胀为代价的,仅仅省去了函数调用的时间开销来提高执行效率。每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。因此,当有一下情况时,不宜使用inline:
①函数体内代码较长,使用内联将导致内存消耗较高
②函数体内存在循环,使用内联将导致执行函数体内代码的时间比函数长
③类的构造函数和析构函数中存在隐藏行为,不适合使用内联函数。不能随便地将构造函数和析构函数地定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联。inline不应该出现在函数的声明中
4、内联函数适用的场合
①取代表达式形式的宏定义
②如果是简单的赋值或返回语句,其使用频率较高,建议使用内联函数。因为函数体简单,消耗的内存较少,节约的时间较高。用最少的代价得到最高的效益
③在类中使用,用来定义存取函数。例:我们定义的类中一般会把数据成员定义成私有或者保护的,这样,外界就不能直接读取类尘缘的数据,对于私有或者保护成员的读写就必须使用成员接口函数来进行。如果我们把这些读写成员函数定义成内联函数的话,能提高效率,例如下面所示的代码:
class A {
private:
int numTest;
public:
int readTest() {
return numTest;
}
void setTest(int i);;
};
inline void A::setTest(int i) {
numTest = i;
}
类A的成员函数readTest()和setTest()都是inline函数。readTest()函数的定义体被放在类声明之中,因而readTest()自动转换成inline函数;setTest()函数的定义体在类声明之外,因此要加上inline关键字。
5、宏定义与内联函数的区别
①内联函数在编译时展开,宏在预编译时展开
②在编译的时候,内联函数可以直接被镶嵌到目标代码中,而宏只是一个简单的文本替换
③内联函数可以完成诸如类型检测、语句是否正确等编译功能,宏就不具有这样的功能
④宏不是函数,内联函数是函数
⑤宏在定义时要小心处理宏参数(一般情况是把参数用括号括起来),否则容易造成二义性。而内联函数定义时不会出现二义性