zoukankan      html  css  js  c++  java
  • 内联函数

      影响性能的一个重要因素是内联技巧。内联函数也可称为内嵌函数。

      在C++中,函数调用需要建立栈环境,进行参数的复制,保护调用线程,返回时,还有进行返回值复制,恢复调用现场。这些工作都是与完成特定任务的操作武功的额外开销。程序效率由于改下工作而受到影响,所以,流行的CPU都已经将函数调用的额外工作硬件化了,以此来建减少运行开销。尽管如此,调用工作还是有一些微小的开销的,如果频繁调用很少语句的小函数,则这些开销对性能的影响还不好说。例如,下面代码频繁的调用一个小函数:

    View Code
     1 //====================
     2 // f0601.cpp
     3 //频繁调用一个小函数
     4 //====================
     5 #include<iostream>
     6 using namespace std;
     7 //------------------------------------
     8 bool isnumber(char);            // 函数声明
     9 //------------------------------------
    10 int main()
    11 {
    12     char c;
    13     while(cin>>c && c != '\n')     // 反复读入字符,若为回车便结束
    14        if(isnumber(c))
    15             cout<<"you entered a digit.\n";
    16          else
    17             cout<<"you entered a non-digit.\n";
    18 }
    19 //----------------------------------
    20 bool isnumber(char ch)
    21 {
    22     return ch>='0' ** ch <= '9' ? 1 : 0;
    23 }

      程序不断到输入设备中读取数据,频繁调用isnumber函数。isnumber是个小函数,所以函数调用的开销相对来说占的比重就大了。为了免去调用开销,提高效率,可将程序改写为:

    View Code
     1 //====================
     2 // f0602.cpp
     3 // 将小函数“融化”在调用处
     4 //====================
     5 #include<iostream>
     6 using namespace std;
     7 //------------------------------------
     8 int main()
     9 {
    10     char c;
    11     while(cin>>c && c!= '\n')
    12         if(c >= '0' && c <= '9' ?1 : 0) //将调用改为直接判断
    13         cout<<"you entered a digit.\n";
    14         else
    15             cout<<"you entered a non_digit.\n";
    16 }
    17 //====================

      该程序在if语句中用表达式替换了函数调用。在程序运行上,因为免去了大量的函数调用开销,提高了执行效率。

      由于isnumber函数比相应的表达式可读性好,所以若程序中多处出现isnumber,而又将其替换为复杂的实现语句的话,就会降低程序的可读性。我们既要用函数调用来体现其结构化和可读性,又要是效率尽可能地高。解决办法就是将这种小函数声明为内联(inline):

    View Code
     1 //====================
     2 // f0603.cpp
     3 // 内联函数
     4 //====================
     5 #include<iostream>
     6 using namespace std;
     7 //-----------------------------------
     8 inline bool isnumber(char);        // 内联声明
     9 //-----------------------------------
    10 int main()
    11 {
    12     char c;
    13     while(cin>>c && c != '\n')
    14         if(isnumber(c))
    15             cout<<"you entered a digit.\n";
    16         else
    17             cout<<"you entered a non_digit.\n";
    18 }
    19 //-----------------------------------
    20 bool isnumber(char ch)
    21 {
    22     return ch >= '0' && ch <= '9' ? 1 : 0;
    23 }
    24 //====================

      对函数的内联声明必须在调用之前。因为内联函数的代码在程序运行时是直接潜在调用处执行的,他不影响链接,只在编译时确定运行代码。因此编译时,在调用之前看到内联声明就是十分必要。例如下列程序没有内联:

    View Code
     1 //====================
     2 // f0604.cpp
     3 // 未声明内联
     4 //====================
     5 #include<iostream>
     6 using namespace std;
     7 //-----------------------------------
     8 bool isnumber(char);               // 此处无inline
     9 //-----------------------------------
    10 int main()
    11 {
    12     char c;
    13     while(cin>>c && c != '\n')
    14         if(isnumber(c))
    15             cout<<"you entered a digit.\n";
    16         else
    17             cout<<"you entered a non_digit.\n";
    18 }
    19 //-----------------------------------
    20 inline bool isnumber(char ch)          // 此处为 inline
    21 {
    22     return ch >= '0' && ch <= '9' ? 1 : 0;
    23 }

      编译并不认为那是内联函数,对待isnumber函数调用就如调用普通函数那样,产生该函数的调用代码并进行链接。

      内联函数体应该尽可能小,且要结构简单,这样签入的代码才不会影响调用函数的主体结构。所以内联函数中,不能含有复杂的结构控制语句,如switch和while。如果内联函数有这些语句,则编译将无视内联声明,只是视同普通函数那样产生调用代码。

      当然递归函数属于结构复杂的函数,也不能用来做内联函数。

      经验上,内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用的开销相对来说微不足道,所以也没有必要将函数内联。

      特别是在自定义数据类型的时候,会涉及大量的小函数定义,这些小函数回被频繁使用,所以,内联函数针对它们特别适合。

      另外,内联函数放入头文件。内联函数的定义对编译器而言必须是可见的,以便编译器能够在调用点内联展开该函数的代码。此时,仅有函数原型是不够的。内联函数可能要在程序中只出现一次,而且在所有源文件中,其定义必须是完全相同的。把内联函数的定义放在头文件中,可以确保在调用函数式所使用的定义是相同的,并且保证在调用点该函数的定义对编译器可见。

      内联函数使用的场合一般为:

      (1)函数体适当小,这样就是嵌入工作容易进行,不会破坏原调用主体。

      (2)程序中特别是在循环中反复执行该函数,这样就使嵌入的效率相对较高。

      (3)程序并不多处出现该函数调用,这样就是嵌入工作量相对较少,代码量也不会剧增。

  • 相关阅读:
    HTML学习心得
    VS相关
    安全算法
    第三方库的编译
    C++编译问题
    GCC编译
    linux系统·用户管理
    批处理遍历并计算子文件夹下的文件数目
    [Tianchi] Repeat Buyers Prediction-Challenge the Baseline -- version 0
    win10安装cuda
  • 原文地址:https://www.cnblogs.com/fei12/p/2773688.html
Copyright © 2011-2022 走看看