当在头文件中定义函数时,如果这个头文件被多个 .cpp
文件包含,那么每个包含该头文件的 .cpp
文件都会有一个该函数的副本。这在链接阶段会引起“多重定义”的错误,因为链接器找到多个相同符号的定义。
使用 inline
关键字可以解决这个问题。当一个函数被声明为 inline
,编译器会尝试将该函数的代码直接嵌入到每个调用点,从而消除了函数调用的开销。更重要的是,inline
函数有特殊的链接规则,允许在多个不同的编译单元中定义相同的函数,只要所有的定义都是相同的。这意味着 inline
函数不会引起链接器的多重定义错误。
因此,当你将头文件中的函数声明为 inline
后,即使这个头文件被多个 .cpp
文件包括,每个 .cpp
文件中的 inline
函数副本在编译后都被视为同一函数,避免了多重定义的问题,从而编译链接成功。这也是为什么加上 inline
后就能编译通过的原因。
在C++中,函数是否声明为 inline
影响其链接行为和代码生成方式。这里我们详细解释一下加不加 inline
在链接期的区别:
1. 非 inline
函数
- 一般定义:非
inline
函数通常在一个.cpp
文件中定义,且每个函数的定义只能存在于一个编译单元中。 - 链接行为:如果一个非
inline
函数被定义在多个编译单元中(即在多个.cpp
文件中重复定义),链接器会报错,提示多重定义(Multiple Definitions)错误。这是因为每个编译单元会生成该函数的一个副本,而链接器在最终链接成一个可执行文件时,无法确定使用哪一个副本。 - 符号表:在编译结束时,每个非
inline
函数在其编译单元的符号表中都有一个条目。链接器需要检查这些符号以确保每个符号只定义一次。
2. inline
函数
- 一般定义:
inline
函数可以在头文件中定义,并且这个头文件可以被多个.cpp
文件包含。 - 链接行为:
inline
函数的多个定义(即使在不同的编译单元中)在链接时被视为同一个函数。这是因为inline
指示符告诉编译器和链接器,每个定义都是相同的,且可以合并。即便实际的机器代码可能被嵌入到每个调用该函数的地方,编译器也保持了对原始函数定义的一个副本,以用于链接时的符号解析。 - 弱符号:通常
inline
函数在编译后被视为“弱符号”(Weak Symbols),这意味着如果链接器在多个编译单元中遇到同名的弱符号,它会选择其中一个,并忽略其他的。这样就避免了多重定义的错误。 - 优化和代码体积:
inline
函数可以减少函数调用的开销,因为编译器可能将函数体直接插入到调用点。然而,过多的inline
可能导致代码膨胀,因为每个调用点都有函数体的一个副本。
总结来说,inline
函数在编写库和头文件时非常有用,因为它们允许在多个编译单元中重用相同的函数定义,而不会导致链接错误。这也有助于模板和头文件中的函数的重用,同时允许编译器进行优化,提高程序的运行效率。