inline和static的区别
在以前面试的时候曾被问道一个问题,cpp中inline关键字的作用是什么,当时只记得inline的语义在c++17标准中进行了修改,而修改后的却忘了,今天在实现单例模式的时候遇到了和inline有关的bug,现在记录一下。
内联
通常在各类教程中会提到,inline的作用是让编译器内联展开所修饰的函数,从而节省跳转函数的开销,在代码中可以测试一下。
1 |
|
编写一段如上的代码,我们声明了永远不要内联这个函数避免编译器的优化。打开反汇编可以看到汇编中有 call TestLineFunction指令,也就是进行了函数跳转,并没有内联。
现在再删去__declspec(noinline),重新编译(需要开启优化)并查看反汇编,call TestLineFunction指令没有了,编译器发现了这个函数可以直接原样复制到main函数中而不需要多余的指令,这个复制函数的动作也就是内联。
现在你应该注意到了,上述的代码中我们并没有使用inline,只是去掉了禁止内联的声明,编译器就自动帮我们进行了内联,既然如此inline关键字的作用是什么?
cpp17
inline关键字最开始是在C99标准中提出,原文提到The intent of the inline specifier is to serve as a hint for the compiler to perform optimizations –inline function specifier,也就是提示(建议)编译器对函数进行内联,既然这不是强制性的要求内联,那么这个关键字的用处其实并不明显,比如上面的例子中是否添加inline并不会修改编译器的行为。
而在cpp17中为inline关键字增添了新的含义,使其可以修饰变量,也就是提供了内联变量,其目的主要是为了解决static
关键字在不同翻译单元会复制变量的问题,接下来给出一个代码示例
1 | // test.h |
1 | // test.cpp |
1 | // main.cpp |
编译运行以上代码,会发现错误
这是因为test.cpp和main.cpp都include了test.h变量,const char* var同时存在于这两个文件,当链接时链接器会发现var这个变量被定义了两次,所以报错,在以前的cpp中,有两种解决方法:
1.在某一个源文件中正常声明变量const char* var
并且初始化它,而在其他源文件中使用extern const char* var
进行声明,但是这种方式无法在头文件中使用,或者说头文件无法被多个源文件引用。
2.使用static const char* var
定义变量,这样就不会出现重定义错误,但是这样会在每一个源文件中保留一份单独变量,即每个源文件中都有同样的一个叫var
的变量。
inline就是为了解决这个问题而出现的,inline修饰的变量在所有源文件中都是同一个变量,运行上述的示例可以得到如下结果:
可以看到main函数中的输出都正确添加了in main
的字符串,而调用函数,也就是test.cpp中的两个输出却不同,static变量并没有添加in mian
,而inline变量正确添加了。
即test函数中输出的static变量是被复制出的单独的一个变量,和main.cpp中被添加in main
的不是同一个变量,而inline在两个文件中都是同一个变量。
总结而言:
static修饰的变量会在不同的源文件中单独保留一份
inline修饰的变量在全局只有同一份,注意inline无法单独修饰局部变量。
此外static和inline可以同时使用,也就是static inline int var
,这样修饰的变量可以在局部函数中声明,并且同时保留了static和inline的特征,也就是生命周期静态,而全局只保留同一份变量。