内联函数是C++的重要函数之一。因此,首先让我们了解为什么使用内联函数,以及内联函数的目的是什么?
当程序执行函数调用指令时,CPU将存储该函数调用之后的指令的内存地址,将函数的参数复制到堆栈上,最后将控制权转移到指定的函数。然后,CPU执行函数代码,将函数返回值存储在预定义的存储位置/寄存器中,并将控制权返回给调用函数。如果函数的执行时间少于从调用者函数到被调用函数(被调用者)的切换时间,则这可能会成为开销。对于大型函数和/或执行复杂任务的函数,与函数运行所花费的时间相比,函数调用的开销通常是微不足道的。但是,对于常用的小型函数而言,进行函数调用所需的时间通常比实际执行函数代码所需的时间要多得多。对于小函数,由于小函数的执行时间少于切换时间,因此会产生开销。
C++提供了一个内联函数,以减少函数调用的开销。内联函数是在调用时在行中扩展的函数。调用内联函数时,将在内联函数调用时插入或替换内联函数的整个代码。此替换由C++编译器在编译时执行。如果内联函数很小,则可以提高效率。
定义函数内联的语法为:
inline return-type function-name(parameters) { // function code }
请记住,内联只是对编译器的请求,而不是命令。编译器可以忽略内联请求。在以下情况下,编译器可能不会执行内联:
1)如果一个函数包含一个循环。 (用于do-while)
2)如果函数包含静态变量。
3)如果函数是递归的。
4)如果函数的返回类型不是void,并且函数主体中不存在return语句。
5)如果函数包含switch或goto语句。
内联函数具有以下优点:
1)不会发生函数调用开销。
2)调用函数时,还可以节省堆栈中push /pop变量的开销。
3)它还节省了从函数返回调用的开销。
4)内联函数时,可以使编译器对函数主体执行特定于上下文的优化。对于普通的函数调用,这种优化是不可能的。通过考虑调用上下文和被调用上下文的流程可以获得其他优化。
5)内联函数可能对于嵌入式系统很有用(如果很小),因为内联函数所产生的代码少于函数调用的前导和返回。
内联函数的缺点:
1)内联函数中添加的变量会占用其他寄存器,在in-lining函数之后,如果要使用寄存器的变量编号增加超过它们的数量,则可能会增加寄存器变量资源利用的开销。这意味着当在函数调用点替换内联函数主体时,该函数使用的变量总数也会被插入。因此,将用于变量的寄存器数量也将增加。因此,如果函数内联后的变量数急剧增加,则肯定会导致寄存器利用率增加。
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) << "\n";
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 << "\n";
}
inline void operation::difference()
{
sub = a-b;
cout << "Difference of two numbers:" << a-b << "\n";
}
inline void operation::product()
{
mul = a*b;
cout << "Product of two numbers:" << a*b << "\n";
}
inline void operation::division()
{
div=a/b;
cout<<"Division of two numbers:"<<a/b<<"\n" ;
}
int main()
{
cout << "Program using inline function\n";
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语言使用宏。预处理程序直接在宏代码内替换所有宏调用。建议始终使用内联函数而不是宏。据C++的创建者Bjarne Stroustrup博士说,在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)Effective C++ , Scott Meyers
2)http://www.parashift.com/c++-faq/inline-and-perf.html
3)http://www.cplusplus.com/forum/articles/20600/
4)Thinking in C++, Volume 1, Bruce Eckel。
5)C++ the complete reference, Herbert Schildt
相关用法
- C++ Inline和Macro的区别用法及代码示例
- C++ Inline和Normal的区别用法及代码示例
- C/C++ fill()、fill_n()用法及代码示例
- C语言 strtok()、strtok_r()用法及代码示例
- C/C++ strdup()、strndup()用法及代码示例
注:本文由纯净天空筛选整理自GeeksforGeeks大神的英文原创作品 Inline Functions in C++。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。