- 如果将函数的实现放在头文件中,那么每一个包含该头文件的cpp文件都将得到一份关于该函数的定义,那么链接器会报函数重定义错误。
- 如果将函数的实现放在头文件,并且标记为 inline 那么每一个包含该头文件的cpp文件都将得到一份关于该函数的定义,并且链接器不会报错。
- 如果将函数的实现放在cpp文件中,并且没有标记为inline,那么该函数可以被连接到其他编译单元中。
- 如果将函数的实现放在cpp文件中,并且标记为inline, 那么该函数对其他编译单元不可见(类似static的效果),也就是其他cpp文件不能链接该函数库,这就是标题中出现的 … undefined reference to …
问题原因就是,编译器在编译一个inline函数时,需要知道其完整定义,如果编译器在本编译单元找不到inline函数定义就会报错(inline
函数的调用不涉及到函数的call,也就不需要链接器参与进来工作,所以也就不会去其他编译单元查找函数定义)。
所以,将类的成员函数的实现放在头文件中不会出现重定义错误,是因为在类中定义成员函数默认为inline函数。
所以下面会报重定义错误(当头文件被两个及以上的cpp文件包含时):
# foo.hpp
class Foo
{
Foo();
};
Foo::Foo() // 实现在头文件中,并且没标记为inline,会报编译错误!
{
}
但是如下这样就不会报错:
# foo.hpp
class Foo
{
Foo() // 默认 inline
{}
};
或者如下这样也不会报错:
# foo.hpp
class Foo
{
inline Foo();
};
Foo::Foo() // 实现在头文件中,并且显示标记为inline
{
}
如果debug的时候,发现自己明明定义了该函数,却还是报 *** undefined reference to ***
错误,那么可以看一下是不是不小心把cpp文件中的函数写成inline了。
不过,将 inline 函数定义在 cpp 文件中有个好处,正如 《大规模c++程序设计》一书中所说,那就是他拥有 internal linkage 属性(不会输出符号到.o文件中),不会污染全局命名空间(类似static函数):
// file1.cpp
int i // external linkage
int max(int a, int b) {return a > b ? a : b;} // external linkage
// file2.cpp
inline int min(int a, int b) {return a < b ? a : b;} // internal
static int fact(int n) {return n<-1 ? 1 : n * fact(n-1);} // internal
其他同样不会输出符号到.o文件中的有:
// file3.cpp
class Link; // 类声明
enum { START_SIZE = 1, GROW_FACTOR = 2}; // 枚举定义
const double PI = 3.1415926595;
static Link *s_root_p;
Link * const s_first_p = s_root_p;
typedef int (PointerToFunc *) ();
#define CASE(x) case X: cout << "x" << endl;
参考 see link: https://stackoverflow.com/questions/34208154/inline-functions-in-cpp-files-of-shared-libraries