C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)

在一切之前,我们需要首先了解C++的编译过程

C++编译链接全过程

C++的编译过程分大致为预编译、编译、链接。

预编译过程

在预编译过程中,会进行一些最基本的操作。

  1. 将会把#define宏定义进行替换。
#define Max 100
如程序中出现了上面的语句,则预编译结束后,程序中的所有 Max 都已经被替换成了100
  1. 执行条件编译:#ifdef,#ifndef,#else,#elif,#endif 等语句
#ifndef XXX
#define XXX
#endif
其实上面的语句相当与一个常用的if判断语句,只不过这个判断语句实在在预编译阶段完成对的
  1. 对#include宏进行替换。也就是把 #include所引用的头文件中的内容原封不动的插入在当前行的位置并把当前这一行替换掉
//main.cpp文件内容
#include "my.h"
void main()
{
}

//my.h文件内容,有一个变量声明和一个函数声明
int age;
void show(void);

//预编译后
int age;		
void show(void);
void main()
{
}
  1. 删除注释,就是加了 // 和 /**/的部分。

编译过程

可以分为两个过程,编译和汇编
编译过程将会检查程序中的语法错误,若没有错误则会将程序转换成汇编语言
汇编过程会将汇编语言的程序转换成机器语言。

注意:此过程涉及分文件编写过程。

C++编译的一个特点就是 分文件编译 ,也就是每一个.cpp文件都会单独进行编译。
也就是说每一个.cpp文件都会单独生成一个目标文件。

但是这样就出现了一个问题:
如果一个木目标文件中的函数调用了另一个目标中的函数怎么办呢??
一个项目,假设预编译之前是这样子的:
C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)_第1张图片
可以看到,main中调用了Person.cpp文件中的func函数。

经过预编译之后会变成下图的样子:
C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)_第2张图片
经过预编译之后,.cpp文件中的 #include “Person.h” 语句都已经被Person.h中的内容给替换掉了。这会让两个.cpp文件分别生成成两个临时文件。
然后对两个文件分别进行编译。
但是在编译过程总发现,临时文件2中调用了func()函数,因为该临时文件中有func函数的声明,编译器可以正常进行语法检查(输入参数和返回值类型等),虽然该文件中并没有函数实现,但这并不会报错,编译器会认为该函数的函数实现被写在了别的文件中。
在其他文件中寻找函数实现的操作会被交给链接器。于是在临时文件2中func()函数会变成一个 特殊符号 ,等待链接器寻找其函数实现。

链接过程

链接过程就是将各个文件最重链接成一个可执行.exe文件的过程。
首先需要合并文件,将各个".obj"文件合并为一个文件,然后找到编译阶段生成的 特殊符号 的实现。最终生成一个可执行文件。

类模的问题

首先模板就是模板,不是数据类型。

硬要是说模板像点什么的话,我感觉它更像是函数。
C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)_第3张图片
像不像一个输入了三个参数(数据类型,还有两个数据)的函数??
类模板像是一个创建类的函数,只不过这个函数的输入参数是 数据类型

就这样吧,我自己的理解,不知对错。

类模板分文件编写存在的问题

首先我们需要知道几个知识:

  1. 编译器使用模板,通过更换模板参数来创建数据类型,这个过程就是 模板实例化
  2. 从模板类创建得到的类型称之为 特例
  3. 模板能否实例化创建特例,不仅仅需要模板的声明,而且还需要模板的定义,以及是否有模板的参数列表,三者缺一不可。因为只有在调用之时才有模板参数列表,所以模板的实例化是迟钝的。
  4. 对了,还有所以函数、变量、类等,都必须先声明再定义最后调用,这毫无疑问。
  5. 若我们不重写类模板的构造方法(使用编译器提供的默认构造方法),同时在其他文件中也不调用模板类的成员方法,是不会报错的。因为,这样做,我们就并没有使用到其他.cpp文件中的东西,自然不会报错。

没有分文件编写

编译过程:
由于在同一个文件中,在编译阶段,编译器首先会找到类模板的声明(用于检查语法错误)。
然后会找到模板的定义。最后找到类模板的使用,会将其实例化。很简单的过程。

分文件编写

首先,我们一般会重写模板类的构造方法,用来进行一些操作(属性赋初值等),所以以下为重写构造方法的情况。

经过预编译程序会由
C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)_第4张图片
变成
C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等)_第5张图片
然后对两个文件分别单独进行编译。

首先,在临时文件1中,有类模板的声明和定义,语法上无错误,但是没办法创建成员函数,也没有具体的模板参数列表,所以类模板中的成员函数此时并没有创建。

然后,在临时文件2中,虽然有类模板的声明,也有函数调用(构造函数),按道理,在这个时候应该创建并调用成员函数。但是因为该文件中没有函数定义(函数定义在临时文件1中),所以无法创建成员函数,也就无法调用。

可是,这并不会报错,因为,编译器认为,成员函数的定义在其他文件中,编译器会把该工作交给链接器来完成,编译器则会使用一个特殊符号来代替这个函数。

接下来是链接阶段,问题就出现了。

由于临时文件2中使用到了一个该文件中不存在的函数,所以链接器会在其他文件中来寻找该函数的定义。但是在临时文件1中,虽然有函数的定义,但是并没有类模板的参数列表,没有参数列表类模板的成员函数是无法创建的,所以在临时文件1中链接器也没有这个函数。所以就会出现未定义符号的错误。

你可能感兴趣的:(C++中类模板分文件编写出现问题的原因(涉及函数定义作用、编译过程等))