1、回答标题的问题
这里说下我自己的理解,如有不正确请各位大佬斧正。想要解决这个问题需要先了解C++代码的编译过程。
C++将代码编译生成可执行文件的过程可以分为三步:预编译 → 编译 → 链接。预编译时,会展开头文件,将包含的头文件的代码整个复制到当前文件中来。编译过程是生成二进制机器指令的过程。C++采用分离式编译,即,在编译时,对预编译源文件生成的文件(obj文件)进行单独编译,和其他文件没有任何关系。这时就有一个问题,当编译一个文件时,遇到了函数func
,当前文件中只有预编译时拉进来的func
的声明,而没有定义,无法生成func
的二进制机器指令。这个问题,链接阶段会专门解决。在编译阶段,这些函数会暂时被标记为 “未解析的外部符号”。当进入链接阶段时, 链接器会专门为所有的未解析符号在其他文件中寻找对应的二进制机器指令,整个工程都链接好,就能正常执行了。
以上说的都是普通C++代码的编译过程。但是模板并不普通。模板只有在真正被使用到的时候才实例化,才生成二进制机器指令。当模板类的定义和实现分别写在了头文件和源文件中,而且main.cpp
中还包含了模板类的头文件。那么编译main.cpp
的obj文件时,势必会遇到模板类成员方法的声明,但是这里没有定义,先标记为未解析的外部符号。到了链接时,根据未解析的外部符号去到模板类源文件的obj文件中寻找其实现,找不到,因为他还没有被实例化,链接失败。此时,如果模板类成员方法的实现也在头文件中,那么就可以解决问题。
2、如何实现定义和实现分开写也能编译
非要把定义和实现分开写在头文件和源文件的话也不是不行。需要在源文件中添加模板类的实例化。
这个方法还是有局限性,因为你不知道类的使用方会套用什么类型,你需要对每个类型在cpp文件中挨个实例化一次,这就很麻烦,尤其是使用方套用的类型是自己定义了一个类的话,还是会出现链接错误。
写这篇文章参考了很多其他文章,下面一一列出。
C++程序是如何编译的?
C++类模板是如何编译的?
C++模板为什么定义和实现必须都写在头文件?
C++模板类如何实现头文件和源文件分离?