前言
C/C++知识点总结文档:inline关键字。该文档总结了C++中inline关键字的特点以及其使用规则。
1. inline介绍
1.1 inline性质
- 若想提升程序的运行性能,一个方法是减少CPU执行指令的次数,而另一个方法则是减少CPU读取内存或硬盘的次数。C/C++函数在被调用时,首先要在栈内存中为形参和局部变量分配存储空间,然后需要将实参的值复制给形参,接下来还需要将函数的返回地址(该地址指明了函数执行结束之后,程序应该回到那里继续执行)存入栈内存中,最后才能进入到函数内部执行。这个过程是需要耗费时间的。
- 另外,函数在返回时,需要从栈内存中回收之前储存的函数形参和局部变量占用的存储空间,然后冲栈内存中取出函数返回地址,再跳转倒该地址继续执行,这个过程也需要耗费时间。
- 在一般情况下,上述的开销可以忽略不计。但是,如果一个函数本身的规模很小,执行时间本来就非常短,那么这个函数调用产生的额外开销和函数本身执行的时间相比就显得不能忽略了。假如这样函数在一个循环中被多次调用执行,函数调用导致的时间开销可能就会使得程序运行明显变慢。
- 为此C++提供了 i n l i n e inline inline 关键字来解决上述的问题。被 i n l i n e inline inline 修饰的函数与普通函数的区别在于:当编译器处理调用 i n l i n e inline inline 函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插入到调用语句处,就像 i n l i n e inline inline 函数被直接实现在了调用处一样。
- 但相对的,使用 i n l i n e inline inline 函数会使最终的程序的体积增加。因此一般情况下 i n l i n e inline inline 关键字会用来修饰只有几条语句的,执行很快的函数。如果一个函数较为复杂,它执行的开销可能远远大于函数调用的额外开销,那么将其作为 i n l i n e inline inline 函数处理的结果是付出让代码体积大大增加的代价,而相对地速度却只获得了微小的提升。这显示是不划算的。
- 有时候函数可能看上去很简单,例如只有一个包含一两条语句的循环体,但该循环的执行次数可能很多,从而导致函数的实际体积很大,需要消耗大量时间。那么这种情况也不适合用 i n l i n e inline inline 关键字来修饰。
1.2 注意事项
- 若需要使用 i n l i n e inline inline 关键字来修饰函数,直接在函数声明的语句最前方加上 i n l i n e inline inline 即可:
inline void Func() {
printf("Hello World");
}
- 由于编译器对被 i n l i n e inline inline 关键字修饰的函数的处理时直接在函数的调用处展开函数,因此编译器要能够在当前源代码文件中找到该函数的具体实现, i n l i n e inline inline的修饰作用才能够生效。也就是说被 i n l i n e inline inline 修饰的函数需要实现在调用处的同一个源代码文件里,或调用处需要 i n c l u d e include include 函数的实现。
- 在很多场景下,函数的声明会写在 .h 文件中,函数的具体实现会写在 .cpp 文件中。在希望调用函数的时候,会先引用(include)包含了函数声明的 .h 然后直接调用。但如果需要调用的
i
n
l
i
n
e
inline
inline 函数也是以这样的形式实现的话,直接调用会导致 LINK2019 的错误:
这是因为编译器在展开 i n l i n e inline inline 函数的时候,虽然能够通过引用的 .h 文件找到 i n l i n e inline inline 函数的声明,却无法读取到函数的具体实现(在这一点上,编译器对 i n l i n e inline inline 函数的链接与对普通函数的链接方式是不一样的)。因此编译器会以为 i n l i n e inline inline 函数没有被实现,从而导致了上述的错误。要解决上述的错误,需要在 i n l i n e inline inline 函数调用的源代码文件里同时引用实现了 i n l i n e inline inline 函数的 .cpp 文件。
1.3 规范化
-
如上述所说,由于编译器需要在展开的 i n l i n e inline inline 函数的地方能够直接访问到函数的实现,导致我们不仅需要引用 .h 文件,同时还要引用 .cpp 文件。这样是不利于代码规范化的。为此C++提供了 .inl 文件来解决上述的问题。.inl 文件是专门用于存放 i n l i n e inline inline 函数实现代码的文件。 i n l i n e inline inline 函数的声明依旧写在 .h 文件里,而函数的具体实现则写在 .inl 文件里,最后在 .h 文件的末尾处引用 .inl 文件即可。编译器在展开 i n l i n e inline inline 函数时,会在源代码引用的 .h 文件中找到 i n l i n e inline inline 函数的声明,紧接着在 .h 文件引用的 .inl 文件中找到函数的具体实现。这样一来就不会报错了。举例如下:
-
.h 文件实现:
// 在MyClass.h文件中
class MyClass
{
void Func1();
inline void Func2();
};
#include "MyClass.inl"
- .cpp 文件实现:
// 在MyClass.cpp文件中
#include "MyClass.h"
void Func1() {
printf("Hello World 1");
}
- .inl 文件实现:
// 在MyClass.inl文件中
inline void Func2() {
printf("Hello World 2");
}