什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
通俗的说,也就是你的程序中有多个源文件,和多个头文件,最后将多个源文件单独编译后形成一个可执行程序的过程,叫做分离编译。
举个例子:
a.h文件
// a.h
#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
a.cpp
// a.cpp
#include"a.h"
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
main.cpp
// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
上面分别是三个文件,两个源文件,一个头文件,我们知道,一个程序的执行它包含四个阶段:
分别是:预处理(去注释、宏替换、头文件展开)->编译->汇编->链接。
预处理做的三件事就是去注释、宏替换、头文件展开
编译阶段就是对程序按照语言特性进行词法、语法、语义、错误检查后生成汇编代码。需要注意的是,头文件不参与编译,编译器对是工程中多个源文件是分开编译的。
汇编阶段是将汇编代码汇编为目标程序(.obj文件)。
链接: 将多个obj文件合并成一个,并处理没有解决的地址问题(如程序中有模板函数,这个时候编译器就会去找该特化后函数的地址)
这时候,如果将上面的三个文件创建成一个工程,那么是跑不过的,他会提示说链接错误。
原因就是:在编译阶段,每个源文件是单独编译,头文件不参与编译。所以就导致了在a.cpp文件下,编译器看他的Add模板函数没有实例化,因此并不会生成具体的Add函数;而在main.c文件中,当执行Add加法语句时,编译器因为没有生成具体的加法函数,所以他找不到对应的函数来执行这条语句,因此就会提示链接错误。
解决办法
1.就是让声明和编译放到一个文件中,为了更加清楚,通常将声明和定义放在一块的文件写成.hpp文件,当然,写成.h文件也是可以的哦!
a.hpp文件
#include<iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
main.cpp文件
#include"a.hpp"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。
只需要将最开始的a.cpp文件中的模板函数显式实例化即可。
a.cpp
#include"a.h"
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
template int Add<int>( const int& left, const int& right);//显式实例化
不过还是推荐第一种方法。
测试环境:VS2017