内联函数是C++的重要特征之一。那么,让我们首先了解为什么使用内联函数,以及内联函数的用途是什么?
当程序执行函数调用指令时,CPU存储函数调用后指令的内存地址,将函数的参数复制到堆栈上,最后将控制权转移到指定的函数。然后,CPU执行函数代码,将函数返回值存储在预定义的内存位置/寄存器中,并将控制权返回给调用函数。如果函数的执行时间小于从调用者函数到被调用者函数(被调用者)的切换时间,这可能会成为开销。对于大型和/或执行复杂任务的函数,与函数运行所需的时间相比,函数调用的开销通常微不足道。然而,对于小的、常用的函数,进行函数调用所需的时间通常比实际执行函数代码所需的时间多得多。这种开销发生在小函数上,因为小函数的执行时间小于切换时间。
C++提供了一个内联函数来减少函数调用开销。内联函数是一个函数,当它被调用时,它会在一行中展开。当内联函数被调用时,内联函数的整个代码会在内联函数调用时被插入或替换。这个替换是由C++编译器在编译时执行的。如果内联函数很小,它可能会提高效率。 定义函数内联的语法是:
inline return-type function-name(parameters) { // function code }
记住,内联只是对编译器的一个请求,而不是命令。编译器可以忽略内联请求。编译器可能不会在以下情况下执行内联: 1) 如果函数包含循环。(暂时,暂时,暂时) 2) 如果函数包含静态变量。 3) 如果函数是递归的。 4) 如果函数返回类型不是void,并且函数体中不存在return语句。 5) 如果函数包含switch或goto语句。
内联函数具有以下优点: 1) 函数调用开销不会发生。 2) 当调用函数时,它还节省了堆栈上push/pop变量的开销。 3) 它还节省了函数返回调用的开销。 4) 内联函数时,可以使编译器对函数体执行特定于上下文的优化。这种优化对于普通函数调用是不可能的。其他优化可以通过考虑调用上下文和被调用上下文的流程来实现。 5) 内联函数对于嵌入式系统可能很有用(如果它很小的话),因为内联函数产生的代码比函数调用前导和返回的代码要少。
内联函数的缺点: 1) 如果将要使用寄存器的变量数量增加,则内联函数中添加的变量会消耗额外的寄存器。如果变量数量增加,则可能会在寄存器变量资源利用率上产生开销。这意味着,当在函数调用点替换内联函数体时,函数使用的变量总数也会被插入。因此,用于变量的寄存器数量也会增加。所以,如果after函数内联变量数急剧增加,那么它肯定会导致寄存器利用率的开销。
2) 如果使用太多的内联函数,那么二进制可执行文件的大小将很大,因为相同代码的重复。
3) 过多的内联也会降低指令缓存命中率,从而降低从缓存到主内存的指令提取速度。
4) 内联函数可能会增加编译时开销如果有人更改内联函数中的代码,则必须重新编译所有调用位置,因为编译器需要再次替换所有代码以反映更改,否则它将继续使用旧功能。
5) 内联函数对于许多嵌入式系统可能没有用处。因为在嵌入式系统中,代码大小比速度更重要。
6) 内联函数可能会导致抖动,因为内联可能会增加二进制可执行文件的大小。内存中的抖动会导致计算机性能下降。
下面的程序演示了内联函数的使用。
#include <iostream> using namespace std; inline int cube( int s) { return s*s*s; } int main() { cout << "The cube of 3 is: " << cube(3) << "" ; return 0; } //Output: The cube of 3 is: 27 |
内联函数和类: 也可以在类内定义内联函数。实际上,类中定义的所有函数都是隐式内联的。因此,这里也应用了内联函数的所有限制。如果需要在类中显式声明内联函数,那么只需在类内声明该函数,并使用inline关键字在类外定义它。 例如:
class S { public : inline int square( int s) // redundant use of inline { // this function is automatically inline // function body } }; |
上述风格被认为是一种糟糕的编程风格。最好的编程风格是只在类中编写函数的原型,并在函数定义中将其指定为内联。 例如:
class S { public : int square( int s); // declare the function }; inline int S::square( int s) // use inline prefix { } |
以下程序演示了这一概念:
#include <iostream> using namespace std; class operation { int a,b,add,sub,mul; float div ; public : void get(); void sum(); void difference(); void product(); void division(); }; inline void operation :: get() { cout << "Enter first value:" ; cin >> a; cout << "Enter second value:" ; cin >> b; } inline void operation :: sum() { add = a+b; cout << "Addition of two numbers: " << a+b << "" ; } inline void operation :: difference() { sub = a-b; cout << "Difference of two numbers: " << a-b << "" ; } inline void operation :: product() { mul = a*b; cout << "Product of two numbers: " << a*b << "" ; } inline void operation ::division() { div =a/b; cout<< "Division of two numbers: " <<a/b<< "" ; } int main() { cout << "Program using inline function" ; operation s; s.get(); s.sum(); s.difference(); s.product(); s.division(); return 0; } |
输出:
Enter first value: 45 Enter second value: 15 Addition of two numbers: 60 Difference of two numbers: 30 Product of two numbers: 675 Division of two numbers: 3
宏有什么问题? 熟悉C语言的读者知道C语言使用宏。预处理器直接替换宏代码中的所有宏调用。建议始终使用内联函数而不是宏。根据Bjarne Stroustrup博士的C++创建者,宏在C++中几乎从来都是不必要的,而且它们容易出错。在C++中使用宏有一些问题。宏无法访问类的私有成员。宏看起来像函数调用,但实际上不是。 例子:
#include <iostream> using namespace std; class S { int m; public : #define MAC(S::m) // error }; |
C++编译器检查内联函数的参数类型,并正确执行必要的转换。预处理器宏无法执行此操作。另一个问题是宏由预处理器管理,内联函数由C++编译器管理。
记住:在类中定义的所有函数都是隐式内联的,C++编译器将执行这些函数的内联调用,但是如果函数是虚函数,则C++编译器不能执行内联。原因是对虚拟函数的调用是在运行时而不是编译时解决的。虚拟意味着等到运行时,而内联意味着在编译期间,如果编译器不知道将调用哪个函数,它如何执行内联?
另一件需要记住的事情是,只有在函数调用期间花费的时间比函数体执行时间更多的情况下,将函数内联才有用。内联函数完全无效的示例:
inline void show() { cout << "value of S = " << S << endl; } |
上述功能的执行时间相对较长。一般来说,执行输入输出(I/O)操作的函数不应定义为内联函数,因为它需要花费大量时间。从技术上讲,show()函数的内联是有限的,因为I/O语句所花费的时间远远超过函数调用的开销。
根据您使用的编译器,如果函数未内联展开,编译器可能会向您显示警告。Java&C等编程语言不支持内联函数。 但在Java中,编译器可以在调用小final方法时执行内联,因为final方法不能被子类重写,对final方法的调用在编译时解决。在C#中,JIT编译器还可以通过内联小函数调用(比如在循环中调用小函数时替换函数体)来优化代码。
最后要记住的是,内联函数是C++的宝贵特性。适当使用内联函数可以提高性能,但如果任意使用内联函数,则无法提供更好的结果。换句话说,不要期望程序有更好的性能。不要让每个函数都内联。最好保持内联函数尽可能小。
参考资料: 1) 有效C++,Scott Meyers 2) http://www.parashift.com/c++-常见问题/在线和性能。html 3) http://www.cplusplus.com/forum/articles/20600/ 4) C++中的思想,第1卷,Bruce Eckel . 5) C++的完整引用,Herbert Schildt
本文由 见见普拉瓦西 。如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请发表评论。