C++ の 内联函数(Inline)


inline 只能用于修饰函数,能够解决一些频繁调用的小函数大量消耗栈内存的问题,是一种提高程序运行效率的手段。

inline 基本

众所周知,函数的调用需要消耗栈内存,每次调用一个函数,就会在栈中分配一片函数所需内存(如参数压栈),但栈内存大小是有限的,频繁使用就会造成因栈内存不足而导致程序出错的问题(经典段错误),比如函数的死循环递归调用。

关键字 inline 所带来的就是在编译时期在函数调用处进行函数体内容展开(代码复制),换句话说就是不用执行正常的进入函数的步骤,直接执行函数体,这样一来就无需进行栈内存的分配。

inline void func1() { std::cout << "inline func1"; }
inline void func2();
void func2() { std::cout << "non-inline func2"; } // inline 必须与定义在一起,否则不生效

int main() {
  func2();     // 等价于写作 std::cout << "inline";
}

乍一看跟宏定义 #define 很像,但 inline 修饰的函数具有类型检查功能,更加安全。

inline 也不是万能的,它只适用于短小精悍,最好只有没几行的函数,并且不能有 whileswitch 这类结构控制语句,更不能是递归函数,因为这类函数最大的特点在于,执行函数体的开销小于调用函数的开销。

毕竟如果调用函数开销相比于直接执行函数体的开销很小或忽略不计,那 inline 就收效甚微了,甚至可能因为代码的复制导致内存消耗反而进一步增大,这是适得其反的。

inline 还可以进行类成员函数的修饰。事实上,在类内定义的函数,除了虚函数以外,均会隐式地视作 inline 函数;类外定义的函数则需要程序员显式 inline,否则当作普通成员函数对待。

class Foo {
  void func1() { ... }             // 类内定义,隐式 inline
  void func2();
};

inline void Foo::func2() { ... }   // 类外定义,显式 inline

by the way,inline 只作为程序员对编译器的建议,具体是否进行内联还是由编译器来决定。如果它决定对一个函数进行内联,那它会执行以下操作:

  1. 将 inline 函数体复制到 inline 函数调用点处;
  2. 为所用 inline 函数中的局部变量分配内存空间;
  3. 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中;
  4. 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 goto)。

上面说到,"在类内定义的函数,除了虚函数以外,均会隐式地视作 inline 函数",那么,虚函数能否用 inline 修饰?

根据上面的讨论我们知道,inline 是编译时期的作为,而当虚函数表现为多态特性时,则需要在运行时进行虚表的查询以及调用,此时如果虚函数为 inline 那就不太合适了——编译器并不知道运行时会调用哪个版本的函数,也就无法执行代码复制。

仅当编译器能够确定调用函数的类时,才能对虚函数应用 inline,比如用对象实例而非指针或引用。


  目录