C++ 内联函数

C++ 内联函数

C++的一个关键特性是内联函数。因此,让我们首先研究内联函数的使用方法和它们的预期应用。如果一个函数被声明为内联函数,那么编译器会在编译时将函数调用位置替换为内联函数的定义。

对内联函数进行任何更改都需要重新编译内联函数,因为编译器需要将所有代码替换为新的代码;否则,将执行旧的功能。

简而言之,当程序执行函数调用指令时,CPU将函数的参数复制到堆栈中,缓存下一条指令的内存地址,然后将控制权交给目标函数。然后,CPU执行函数的代码,将返回值保存在指定的内存地址或寄存器中,并将控制权交还给调用了函数的函数。如果函数的执行时间短于从调用函数切换到被调用函数(被调用者)所需的时间,这可能会导致开销。与运行大型或复杂函数所需的时间相比,函数调用的开销通常是微不足道的。然而,调用小型、频繁使用的函数所需的时间往往远远超过运行函数的代码所需的时间。由于它们的执行时间小于切换时间,小型函数会遇到这种开销。为了最小化函数调用的开销,C++提供了内联函数。当调用一个函数时,它会在行内展开,成为内联函数。当调用内联函数时,它的整个代码体会在内联函数调用的位置被添加或替换。C++编译器在编译时进行此替换。如果内联函数很小,则可以通过内联函数增加效率。对于编译器来说,内联只是一个请求,而不是命令。编译器可能会拒绝内联请求。编译器可能在如下情况下不进行内联实现:

  1. 如果函数包含循环(for、while、do-while)。
  2. 如果函数有静态变量。
  3. 如果函数递归调用。
  4. 如果函数体中没有返回语句,并且函数的返回类型不是void。
  5. 如果函数使用了goto或switch语句。

内联函数的语法:

inline return_type function_name(parameters)
{
   // function code?
} 

让我们了解普通函数和内联函数之间的区别。

main() 方法中,当调用函数fun1()时,控制权会转移到被调用函数的定义上。函数调用和函数定义的地址是不同的。这个控制转移会花费很多时间,并增加额外的开销。

当遇到内联函数时,函数的定义会被复制到它的位置上。在这种情况下,没有控制转移,这样可以节省很多时间并减少额外开销。

让我们通过一个示例来了解。

#include 
using namespace std;
inline int add(int a, int b)
{
    return(a+b);
}
int main()
{
cout<<"Addition of 'a' and 'b' is:"<

一旦编译完成,代码将会如下所示:

#include
using namespace std;
inline int add(int a, int b)
{
     return(a+b); 
}
int main()
{
cout<<"Addition of 'a' and 'b' is:"<

为什么我们需要在C++中使用内联函数?

C++中内联函数的主要用途是节省内存空间。每当函数被调用时,执行任务(如移动到调用函数)需要很长时间。如果函数的长度很小,那么执行时间中的大部分时间将花费在这些开销上,有时,移动到调用函数所需的时间将大于执行该函数所需的时间。

解决这个问题的方法是使用宏定义,也称为宏。预处理器宏在C中被广泛使用,但是宏的主要缺点是它们不是普通的函数,这意味着在编译期间不会进行错误检查。

C++为这个问题提供了一种解决方案。在函数调用的情况下,调用这样小的函数所花费的时间是巨大的,所以为了解决这个问题,引入了一种称为内联函数的新概念。当在main()方法中遇到该函数时,将其与定义展开,从而节省时间。

在以下情况下,我们无法为函数提供内联:

  • 如果一个函数是递归的。
  • 如果一个函数包含像for、while、do-while循环这样的循环。
  • 如果一个函数包含静态变量。
  • 如果一个函数包含switch或go到语句。

我们何时需要内联函数?

内联函数可以在以下场景中使用:

  • 当需要性能时可以使用内联函数。
  • 它可以用于宏。
  • 我们可以在类外部使用内联函数,以隐藏函数的内部实现。

内联函数的优点

  • 在内联函数中,不需要调用函数,因此不会产生任何额外开销。
  • 它还节省了从函数返回语句的开销。
  • 它不需要任何栈,在栈上推入或弹出变量,因为它不执行任何函数调用。
  • 内联函数对于嵌入式系统特别有益,因为它比普通函数产生的代码更少。

内联函数的缺点

内联函数的缺点有:

  • 没有解释吗,保留HTML格式:
    • 在内联函数中创建的变量将消耗额外的寄存器。如果变量增加,则寄存器的使用也会增加,这可能会增加寄存器变量资源利用的开销。这意味着当函数调用被替换为内联函数体时,变量的数量也会增加,导致寄存器的数量增加。这会对资源利用造成开销。
    • 如果使用许多内联函数,则二进制可执行文件也会变得很大。
    • 使用这么多的内联函数可能会降低指令缓存命中率,从缓存内存中获取指令的速度降低到主存储器的速度。
    • 这也增加了编译时的开销,因为每当在内联函数内进行更改时,代码需要重新编译以反映更改;否则,它将执行旧功能。
    • 有时内联函数对于许多嵌入式系统而言并不实用,因为在某些情况下,嵌入式的大小被认为比速度更重要。
    • 它还会导致抖动,原因是二进制可执行文件的大小增加。如果内存中发生抖动,那么会导致计算机性能下降。

宏有什么问题

C语言的熟悉读者知道它使用宏。宏代码中的所有直接宏调用都将被预处理器替换。建议始终使用内联函数而不是宏。C++的发明者Bjarne Stroustrup博士声称在C++中很少需要使用宏,并且会出现错误。在C++中使用宏并非没有问题。私有类成员对宏是不可访问的。虽然宏类似于函数调用,但其实并非如此。

示例

#include 
using namespace std;
class S

{
    int m;
public:
#define MAC(S::m) // error
};

C++编译器会验证必要的类型转换是否完成,并且内联函数的参数类型是否有效。预处理宏无法达到这个目的。此外,预处理器管理宏,而C++编译器管理内联函数。请记住,虽然在类内指定的所有函数都隐式地内联,而C++编译器将执行对这些函数的内联调用,但如果函数是虚函数,则无法进行内联。原因是虚函数的调用在运行时解析,而不是在编译时解析。如果编译器不知道将调用哪个函数,那么在编译期间如何进行内联,而虚函数意味着等待运行时呢?另一个需要记住的是,只有当调用函数所需的时间长于执行函数体所需的时间时,将函数移入内联才是有益的。举个示例,考虑以下情况:

示例

inline void show()
{
    cout << "value of S = " << S << endl;

}

执行上述函数需要一些时间。基本规则是,执行输入输出(I/O)操作的函数不应定义为内联函数,因为它会花费很多时间。由于I/O语句所需的时间比函数调用要长得多,从技术上讲,将show()函数内联化只有有限的效果。

如果函数不被内联展开,在使用不同编译器时可能会出现警告。内联函数在像Java或C#这样的编程语言中无法使用。

但是因为final方法不能被子类重写,并且对final方法的调用在编译时处理,当调用小的final过程时,Java编译器可以进行内联处理。通过内联短函数调用,即时编译器可以进一步优化C#代码(例如在循环中调用时替换小函数的主体)。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程