什么是分离式编译
通俗的来讲就是将声明和定义分离在不同文件中
一个程序由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有
目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
正常函数与模板分离式编译
看代码,先区分两个
正常函数的分离式编译
//------------------------ Test.h 存放函数的声明-----------------------
#pragma once
#include "iostream"
using namespace std;
//正常函数的声明
int Add(const int& num1, const int& num2);
//------------------------ Test.h-----------------------
//------------------------ Test.cpp 存放函数的定义 -----------------------
#include"Test.h"
int Add(const int& num1, const int& num2)
{
cout << "int Add(const int& num1, const int& num2)" << endl;
return 0;
}
//------------------------ Test.cpp -----------------------
//------------------------ main.cpp -----------------------
#include"Test.h"
int main()
{
Add(1, 1);
return 0;
}
//------------------------ main.cpp -----------------------
模板的分离式编译
//------------------------ Test.h 存放声明-----------------------
#pragma once
#include "iostream"
using namespace std;
//函数模板的声明
template <class T>
T Add(const T& num1, const T& num2);
//------------------------ Test.h-----------------------
//------------------------ Test.cpp 存放定义 -----------------------
#include"Test.h"
template<class T>
T Add(const T& num1, const T& num2)
{
cout << "T Add(const T& num1, const T& num2)" << endl;
}
//------------------------ Test.cpp -----------------------
//------------------------ main.cpp -----------------------
#include"Test.h"
int main()
{
Add(1, 1);
return 0;
}
//------------------------ main.cpp -----------------------
此时我们看到出现了一个链接错误,那么问题来了:为什么模板的分离式编译会出现错误?为什么会出现这个错误?怎么解决模板分离式编译的问题呢?
为什么模板分离式编译会出错?
C++Primer中这么说:当使用一个vector这样的泛型类型,或者find这样的泛型函数时,我们提供足够的信息,将模板转化为特定的类或者函数(实例化)。这样的转换发生在编译阶段
我们知道了模板的实例化发生在编译阶段
回顾编译过程
- 预处理阶段
头文件展开、条件编译指令、宏替换 - 编译阶段
将第一步产生的文件同其他文件一起编译成汇编代码 - 汇编阶段
将汇编源码转换成可重定位目标文件 - 链接阶段
进行符号解析和重定位,将可重定位目标文件链接成可执行目标文件
我们通过编译过程中的第一个环节:预处理之后,此时就把.h文件展开到.cpp文件当中,所以此时两个.cpp文件里面就分别包含了函数模板的声明(Test.cpp中包含了函数模板的声明和实现,main.cpp中包含了函数模板的声明和调用),这两个文件经过编译之后会生成汇编代码,经过汇编之后会生成符号表,但是在汇编这一步中,编译器会生成符号表,符号表里面存着各各函数的地址,在链接的时候由main函数中调用的Add函数去符号表里寻找地址,这时出错了! 符号表里面这时没有存Add的地址, 因为在编译过程中两个文件是独立的,正因为是独立的,在编译阶段模板才没能够进行实例化,因此符号表中并不会生成Add的地址,所以分离编译模板在链接时出错了,因为链接时会去调用Add,但是没有地址,链接出错。
解决方案:
1、显式实例化
//在声明的.cpp文件里面显式实例化模板
#include"Test.h"
template<class T>
T Add(const T& num1, const T& num2)
{
cout << "T Add(const T& num1, const T& num2)" << endl;
return num1 + num2;
}
//显式实例化模板
template
int Add<int>(const int& num1, const int& num2);
缺陷:显式实例化只能实例化一种类型,当遇到操作数是其他类型,还需要进行其他类型的显式实例化
1、分离式编译
将声明和定义放在同一个.h文件里面,这样预处理的时候就可以让模板的声明、定义、调用出现在一个.cpp文件中,进而编译过程就可以实例化模板