自学内容网 自学内容网

分离编译(介绍,解决“类模板定义和声明不在同一文件导致链接错误“的问题),类模板实例化原理,

目录

分离编译

介绍

问题代码示例 

代码

说明

预处理

编译

链接

类模板实例化原理

总结

解决方法 

显式实例化

模板的声明和定义放在一个头文件


分离编译

介绍

分离编译是一种编程技术

  • 允许将程序代码分割成多个文件,每个文件可以独立地编译成目标文件(.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)!