分离编译(介绍,解决“类模板定义和声明不在同一文件导致链接错误“的问题),类模板实例化原理,
目录
分离编译
介绍
分离编译是一种编程技术
- 允许将程序代码分割成多个文件,每个文件可以独立地编译成目标文件(.o文件)
- 然后将这些目标文件链接在一起, 来创建最终的可执行程序
分离编译的核心思想 -- 将代码逻辑分解为多个独立的模块,每个模块可以单独编译
- 当程序代码发生改动时,仅需重新编译那些受影响的源文件,而无需重新编译整个项目
问题代码示例
一般来说,分离编译虽然很方便,但也很容易出现不少问题
代码
比如下面的三个文件
stack.h:
stack.cpp:
main.cpp:
说明
预处理
在这三个文件形成可执行文件的过程中
- 首先是在两个cpp文件中的.h文件展开,形成.i文件
这样stack.i包括:
- stack类的全部声明+定义
- A类的func1定义
但main.i中包括:
- stack类的所有声明+部分定义
- A类的声明
编译
所以在编译阶段:
- 只能确定 main.i中size() 的地址
- 以及stack类的构造函数的地址 (这里只是生成了与地址相关的信息,但这些信息还没有映射到最终的绝对地址 , 编译阶段的地址是为后续阶段提供信息的一种中间状态)
- 以及生成修饰后的函数名
虽然没有确定所有函数的地址,但是编译是可以通过的,因为含有对应函数的声明
接下来是汇编阶段,生成.o文件
上面这三个阶段完成后,对应生成地址的情况如下图:
链接
接下来进行链接
- 链接是可以将func1的地址确定的
- 因为func1的定义有在stack.cpp中,俩文件相互一交流(拿着修饰后的函数去符号表找),就可以确定辽
但素,这样的代码会报错,编译器说push在链接中出现问题
- 原因是在main.s中,push修饰后的函数名是和int有关的(因为实例化传入的类型就是int)
- 但是在stack.s中,没有人告诉他要实例化成什么,所以并没有实际生成代码,只是一个壳子,也就没有什么地址,自然在符号表中找不着
类模板实例化原理
在源文件中使用模板时:
- 需要提供模板参数,并创建一个模板的实例
- 这样就会告诉编译器 -- 我们想要使用特定类型来实例化模板
于是,编译器会生成一个用于在实际需要时实例化代码的计划
- 这个计划称为模板的实例化
- 这个计划会记录下特定模板参数,以及生成实际代码的需要
当编译器在编译源文件时
- 会根据计划生成特定类型的类代码,其中包含了特定类型的成员函数和数据成员
总结
所以,上面的情况之所以出现问题,就是因为类的具体代码不在当前编译单元中
- 模板的实现需要在编译时可见
- 如果模板的实现不在当前编译单元(实例化了模板)中,那么编译器无法实例化模板,从而导致链接错误
解决方法
显式实例化
如果一定要把类的声明和定义放在两个文件,可以采用显式实例化模板的方式
- 但是坏处就是,一旦使用新的类型实例化模板,就得加代码,hin麻烦
模板的声明和定义放在一个头文件
这样在包括头文件的源文件中:
- 一旦源文件实例化了类,就能及时的在编译过程生成实际代码,然后可以链接成功
原文地址:https://blog.csdn.net/m0_74206992/article/details/143609954
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!