内联函数
内联函数是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展;也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省每次调用函数时带来的额外时间开支。 使用内联函数时必须考虑程序的占用空间和执行效率。
inline
C++中通过inline 修饰符来建议编译器生成内联函数。
内联成员函数
在类声明中定义的成员函数,除了虚函数外,默认都是隐式地内联的。
class A {
int Fun() { return 0; }
}
class A {
int Fun();
}
inline int A::Fun() { return 0; }
特性
内联函数相比于普通函数
优点
- 当函数体较小的时候,内联可以令目标代码更加高效。inline函数在被调用处进行代码展开,省去了调用普通函数会产生的参数压栈、栈帧开辟与回收、结果返回等步骤,以此来提高程序运行速度。
缺点
- inline函数是以代码膨胀为代价来消除函数调用带来的开销的,意味着消耗更多的内存空间。
- inline函数在变更实现代码后需要重新链接。非内联函数则不需要。
- 内联是不能完全由程序控制。内联函数只是对编译器的建议,最终由编译器决定是否内联。
内联函数相比于函数式宏定义
夹带着宏实参的函数式宏定义(Function-like Macro)是通过对文本直接替换的方式,同样是在调用处展开代码来避免函数调用的开销。 宏只做预处理而不编译,不会检查程序是否符合C语法。
函数式宏定义本身不必编译生成指令,但是代码中出现的每次调用所编译生成的指令都相当于一个函数体。
优点
- inline函数具有同一般函数的所有可预料行为。
考虑下面这种情况:#define GET_MAX(a, b) ((a) > (b) ? (a) : (b))
int a = 5, b = 0;
GET_MAX(++a, b);
GET_MAX(++a, b + 10);
调用函数时先求实参表达式的值再传给形参,如果实参表达式有Side Effect,那么这些Side Effect只发生一次。但宏只是简单的文本替换,所以宏中的 a 都会被替换为 ++a 。由于 ++ a 比 b 大,所以第一个调用中的a会被累加两次。而 ++a 又比 b + 10 要小,所有第二个调用中的 a 只会被累加一次。文本替换难以做到与一般函数的行为一致。 - 在代码展开时会做类型安全性检查。函数式宏定义的参数没有类型,预处理器只负责做形式上的替换,而不做参数类型检查,所以传参时要格外小心。
- 遵守函数的作用域和访问规则。如内联函数可以访问类的成员变量,宏定义则不能。
- 内联函数是可调试的,宏定义则不可以。
使用建议
- 对于存取函数以及其他函数体比较短,性能关键的函数,推荐使用内联。一个较为合理的经验准则是,不要内联超过10行的函数。
- 谨慎对待析构函数,析构函数往往比其表面看起来更长,因为有隐含的类成员和基类的析构函数被调用。
- 内联包含循环或
switch 语句的函数往往是得不偿失的。因为光循环和switch语句本身就有一定的代码量。 - 递归函数通常不会被内联。因为递归层数在编译时可能是未知的。大多数编译器都不支持内联递归函数。
- 当虚函数表现多态性的时候不能内联。其他情况下是可以内联的。内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译期无法知道运行期调用哪个代码。inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类,这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。
其他
C++17标准新加入了inline可用于修饰变量的规则。不过不在内联函数范围内,有机会额外探讨。
|