inline函数

内联函数的引入

求两个整数的最大值
  - 使用条件选择运算符 ?:
      x > y ? x : y
  - 使用函数进行封装  
      int max(int x, int y)
      {
        return x > y ? x : y;
      } 
将一个小的操作定义成一个函数的好处
  - 阅读和理解函数的调用,要比读一条等价的条件表达式并解释它的含义要容易
      如求两个整数的最大值,可以通过函数的名字max,较容易的理解函数的功能
  - 如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易
      修改表达式,需要找到需要修改的表达式的位置,如果通过函数进行封装调用,只需要修改函数里的表达式即可
  - 使用函数可以确保统一的行为,每个测试都能够保证以相同的方式实现
  - 函数可以重用,不必为其他应用程序重写代码
操作写成函数的缺点
  调用函数比求解等价表达式消耗的时间长
    大多数机器上,调用函数需要很多的工作::调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行
      对于简短的语句使用函数的开销较大
  在C语言中,我们使用带参数的宏定义这种借助编译器的优化技术来减少程序的执行时间
  在C++中,使用内联函数,优化编译器,降低运行时间

内联函数的定义

  内联函数是C++的增强特性之一,用来降低程序的运行时间。
  当内联函数收到编译器的指示时,即可发生内联:编译器将使用函数的定义体来替代函数调用语句,这种替代行为发生在编译阶段而非程序运行阶段。
  定义函数时,在函数的最前面以关键字“inline”声明函数,即可使函数称为内联声明函数。
内联函数的语法构成
inline 返回值类型 函数名(参数列表)
{
  函数体;
}

内联函数与带参数的宏定义

带参数的宏定义的原理
  编译器在预处理阶段,将使用宏定义的地方的代码,进行完整替换
带参数的宏定义的缺点
  - 容易出错,预处理器在拷贝宏代码时常常产生意想不到的边际效应
      - 建议在使用宏代码时,添加一个括号,尽可能的避免边际效应
  - 不可调试,内联函数是可以调试的
  - 对于C++而言,宏代码无法操作类的私有成员

内联函数的“工作”步骤

  对任何内联函数,编译器在符号表里放入函数的声明,包括名字、参数类型、返回值类型(符号表是编译器用来收集和保存字面常量和某些符号常量的地方)。
  如果编译器没有发现内联函数存在错误,那么该函数的代码也被放入符号表里面。
  在调用一个内联函数时,编译器首先检查调用是否正确(进行类型安全检查、或者进行自动类型转换,对所有的函数都一样)。
  如果正确,内联函数的代码就会直接替换函数调用语句,于是省去了函数调用的开销。
  这个过程与预处理器有更加显著的不同,因为预处理器不能进行类型安全检查和自动类型转换。
  假设内联函数是成员函数,对象的地址(this)会被放在合适的地方,这也是预处理器办不到的
内联函数的优点
  - 内联函数可以进行调试
  - 函数被内联后,编译器可以通过上下文相关的优化技术对结果代码执行更深入的优化
内联函数的使用
  关键字inline必须与函数定义放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用
    C++ inlline函数是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。
    一般地,用户可以阅读函数的声明,但看不到函数的定义。
    一种高质量设计C++/C的原则:声明与定义不可混为一谈,用户没有必要,也不应该知道函数是否需要内联。
内联函数的位置对比
  内联函数定义可以放在源文件中,但此时只有定义的那个源文件可以用它,而且必须为每个源文件拷贝一份定义(即每个源文件里的定义必须是完全相同的)。
  内联函数可以放在头文件中,也是对每个定义做一份拷贝,只不过是编译器替你完成拷贝。
  内联函数放在头文件相比于放在源文件中,放在头文件中既能够确保调用函数是定义是相同的,又能够保证在调用点能够找到函数定义从而完成内联(替换)。
  内联函数不同于其他函数,编译器在调用点内联展开函数代码时,必须能够找到inline函数的定义才能将调用函数替换为函数代码,对于头文件中仅有函数声明是不够的。
内联函数使用的注意事项
内联函数可以提高函数的执行效率,为什么不把所有的函数都定义为内联函数呢?
  内联是以代码膨胀(拷贝)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
    注意:这里的“函数调用开销”并不包括执行函数体所需要的开销,而是仅指参数压栈、跳转、退栈和返回等操作。
  - 如果执行函数体内代码的时间比函数调用的开销大得多,那么inline的效率收益会很小
  - 每一处内联函数的调用都要拷贝代码,将使程序的总代码量增大,消耗更多的内存空间
不宜使用内联函数的情况
  - 如果函数体内的代码过长,使用内联将导致可执行代码膨胀过大
  - 如果函数体内出现循环或者其他复杂的控制结构,那么执行函数体内代码的时间将比函数开销大得多,此时内联的意义不大
内联函数与构造函数和析构函数
  构造函数和析构函数会隐藏一些行为,如“偷偷地”调用基类或成员对象的构造函数和析构函数
  所以不要轻易让构造函数和析构函数成为内联函数

总结

  inline在实现的时候就是对编译器的一种请求,因此编译器完全有权利取消一个函数的内联请求。
  一个好的编译器能够根据函数的定义体,自动取消不值得的内联,或者自动地内联一些没有inline请求的函数。
  因此,编译器往往选择短小而简单的函数来内联(内联候选函数)
原文地址:https://www.cnblogs.com/xkyrl/p/14669218.html