在一个编译单元里,我们假设有三个文件,test.h test.cpp main.cpp。test.h文件里装有各个函数的声明,而实现在test.cpp中,main.cpp中实例化函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(linker)进行连接成为一个.exe文件。
test.h
void FUNC();
===========================
test.cpp
#include"test.h"
void FUNC()
{
dosomthing;
}
===========================
main.cpp
#include"test.h"
int main()
{
FUNC();
return 0;
}
其实在执行此程序时会发生一下过程。
test.cpp 与 main.cpp在编译的过程中,成为test.obj和 main.obj,在执行main.cpp中调用了FUNC()函数,此时编译器仅仅知道main.cpp中所包含的test.h文件中一个关于FUNC()函数的声明。编译器将这里FUNC()看做外部链接类型,就是认为他的函数实现在另一个.obj文件中,编译时main.cpp中对FUNC()的调用只会生成一句call指令,此时的call指令其实是有问题的,因为main.cpp中没有实现FUNC()的代码。
这是连接器在其他的.obj文件中寻找FUNC()的实现代码,找到以后将call FUNC()的指令调运的地址换成实际FUNC()的进入点地址,注意的是:连接器实际上是将工程中的.obj连接为.exe,关键是在寻找外部链接符合在另一个.obj中的地址,然后替换原来的地址。编译main.cpp时,编译器不知道FUNC的实现,所以当碰到对它的调用时只是给出一个指示,指示连接器应该为它寻找f的实现体。这也就是说main.obj中没有关于f的任何一行二进制代码。
编译test.cpp时,编译器找到了FUNC的实现。于是乎FUNC的实现(二进制代码)出现在test.obj里。
连接时,连接器在test.obj中找到FUNC的实现代码(二进制)的地址(通过符号导出表)。然后将main.obj中悬而未决的call XXX地址改成f实际的地址。完成。
然而模板却又不同
模板函数的代码并不能直接编译成二进制代码,而要有一个实例化的过程。
test.h
template<class T>
class A
{
public:
void f();//只声明
}
test.cpp
#include"test.h"
template<class T>
void A<T> f()
{
dosomthing;
}
main.cpp
#include"test.h"
int main()
{
A<int> a;
f();
}
编译器在main.cpp中并不知道Aa的定义,因为它并不在test.h中,而当连接器在其他的.obj中寻找时,也并没有,因为模板只有用到的时候才会被实例化出来,显然test.cpp中并没有用,于是只能给出错误。
解决方法
在模板头文件 x x x . h 里面显示实例化-> 模板类的定义后面添加 template class AA; 一般不推荐这种方法,一方面老编
译器可能不支持,另一方面实例化依赖调用者。(不推荐)
2 . 将声明和定义放到一个文件 ” x x x . hpp” 里面,推荐使用这种方法。