Cpp-Inline_Function

97 阅读1分钟

Inline_Function


什么是内联函数

首先需要清楚,内联函数仅仅是对编译器的一个建议,希望可以将函数直接展开,这样可以不用开辟函数栈帧和参数压栈等一系列操作。但内联函数一般只适用于简短且调用频繁的函数。


内联函数的性质

内联函数的定义方法

inline int add(int a, int b) {return a + b;}

只需要在函数前加上 inline 关键字即可,但编译器不一定会展开函数,一般编译器会根据函数的复杂情况和调用情况来决定是否展开。

内联函数声明定义不可分离

以下用三个文件举例:head.h func.cpp test.cpp

//head.h
#pragma once

#include <iostream>

inline void func();
//func.cpp
#include "head.h"

void func()
{
	std::cout << "Thepale";
}
//test.cpp
#include "head.h"

int main()
{
	func();

	return 0;
}

运行时发生链接错误:test.obj : error LNK2001: 无法解析的外部符号 "void __cdecl func(void)" (?func@@YAXXZ)

在此函数结束编译阶段时, 由于此时 func() 函数只有声明没有定义,则会在链接阶段用通过函数名修饰规则生成的函数名去符号表中寻找,但由于在 func.cpp 中头文件展开后,编译时已经确定 func() 为内联函数(不管是声明中加 inline 还是定义中加 inline 都一样,在这时都被确定为内联函数),内联函数不生成地址,不存在于符号表中,故发生找不到 func() 的链接错误。

内联函数不适用于复杂的长函数和递归函数

一般编译器会设置函数长度检查,如果过长的代码不会选择内联展开。且递归函数不会进行内联展开。如果将长函数展开,可能会导致代码膨胀:假如函数代码有1000 行,总共有 1000 处调用该函数(非循环情况,这里指的是分别调用),若展开,则会生成 1000 * 1000 = 1000000 行代码,导致代码膨胀,而采用函数调用则没有这样的问题。

如果将递归函数展开,可能出现无限展开的情况,最后将可能导致栈溢出。即使不无限展开,多数的递归函数展开后代码量和复杂程度也是巨大的,对编译器的优化要求也是非常高的,这里不多加赘述。

默认内联的函数

类中的成员函数:

class Thepale
{
public:
	void func() {return 0;}    
}

使用函数模板的函数:

template <class T>
T func(T e) {return e};

(或使用函数模板的类中的成员函数)

以上函数都默认有 inline 属性,即使不添加 inline,编译器也会默认加上。


总结

  • 内联函数只是对编译器的建议,具体情况需要编译器决定。
  • 内联函数是一种以空间换时间的操作。
  • 内联函数声明和定义不可分离。
  • 内联函数不适用于长函数和递归函数。
  • 使用函数模板和类中的成员函数默认具有 inline 属性。

补充说明

  • 函数的声明和定义在同一文件中不叫做声明和定义分离,只有在不同文件中,需要包含使用时才称为声明和定义分离。
  • 内联函数在 Debug 状态通常不会展开,因为 Debug 注重细节展示,关闭优化。