博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
模板的分离式编译
阅读量:5364 次
发布时间:2019-06-15

本文共 2999 字,大约阅读时间需要 9 分钟。

分离式编译是指一个完整的程序或项目由若干个源文件共同实现,每个源文件单独编译生成目标文件,最后将该项目中的所有目标文件连接成一个单一的可执行文件的过程。

    每个.cpp源文件经过预处理,它所包含的.h文件的代码都会被展开到其中。再经过编译器的编译汇编等过程,将该.cpp文件转变为.obj文件,这是此文件已经变为二进制文件,本身包含的就是二进制代码。这时,该文件还不一定能够执行,因为并不保证其中一定有main函数,或者该源文件中的函数可能引用了另一个源文件中定义的某个变量或者函数调用,又或者在程序中可能调用了某个库文件中的函数,等等。这些都要通过链接器将该项目中的所有目标文件连接成一个单一的可执行文件来解决。

举个例子:

//****************************** Test.h *************************************/ #pragma once #include
using namespace std;#include
void fun(); // 仅仅只声明 //****************************** Test.h *************************************/ 
//****************************** Test.cpp *************************************/ #include"Test.h" void fun()                 // 对函数fun()进行定义{    cout << "fun" << endl;             } //****************************** Test.cpp *************************************/ 
//****************************** main.cpp *************************************/ #include"Test.h" int main(){    fun();              //调用fun()函数    system("pause");    return 0;} //****************************** Test.cpp *************************************/

 

     

      

  main.cpp包含的Test.h头文件只有对fun()函数的声明,所以在main.cpp中没有任何与fun()函数定义相关的代码,这里就会把fun函数看做为外部链接类型。在主函数中调用fun函数时,会产生如上图的红框中的call命令,当然这里的地址是一个虚假的地址。链接器在Test.obj文件中找到fun函数的实现代码,将call fun地址通过jmp指令切换成真正的fun函数地址。

再看模板的分离编译:

///************************** SeqList.h **************************//#include
using namespace std;#include
// 定义模板类 (类型参数为T)template
class SeqList{public: SeqList(); //构造函数声明private: T* _array; size_t _size; };
///************************** SeqList.cpp **************************// #include"SeqList.h" template
//模板声明SeqList
::SeqList() //在类模板体外定义构造函数 :_array(NULL) , _size(0){}
///************************** Test.cpp **************************// #include"SeqList.h"  int main(){    SeqList
list1; system("pause"); return 0;}

编译链接会出以下错误:



1>------ 已启动生成:  项目: 4_4, 配置: Debug Win32 ------

1>  Test.cpp

1>  SeqList.cpp

1>  正在生成代码...

1>Test.obj : error LNK2019: 无法解析的外部符号 "public: __thiscall SeqList<int>::SeqList<int>(void)" (??0?$SeqList@H@@QAE@XZ),该符号在函数 "void __cdecl Test(void)" (?Test@@YAXXZ) 中被引用

1>E:\CODE\4_4\Debug\4_4.exe : fatal error LNK1120: 1 个无法解析的外部命令

========== 生成:  成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========



这是因为编译时SeqList<T>没有实例化出SeqList<int>实例,所以链接时出错。

    到底是怎么回事呢?

    对于模板来说,模板只有被调用的时候才会被实例化,如果不被调用它是不会实例化的。因此,如果你不调用某模板函数或模板类时,该模板中的许多语法错误编译器是懒得检查的。

    在执行主函数中的SeqList<int> list1语句时,要调用SeqList<int>的构造函数,链接器在Test.obj中找不到此构造函数的定义,于是在SeqList.obj文件中找。大家如果认为SeqList<int>的构造函数的实现代码就在SeqList.obj中,那就错了。模板只有被调用的时候才会被实例化! SeqList.cpp中没有任何对此构造函数的调用,因此SeqList.obj中是没有任何有关SeqList<int>构造函数实现的二进制代码(因为它没有被实例化),链接器找不到函数实现的二进制代码,因此只能报错。

    如果我们在SeqList.cpp中添加对SeqList<int>构造函数的调用,编译器就会将构造函数实例化,产生相应的二进制代码,这时候链接器就会在SeqList.obj文件中找此构造函数的定义。程序编译就不会出错了。

     当然,我们也可以将声明和定义放到一个文件 "xxx.hpp" 里面,来解决这类问题。

 

附:

转载于:https://www.cnblogs.com/Lynn-Zhang/p/5377132.html

你可能感兴趣的文章
[转].net自定义configSections的5个示例
查看>>
[转]逐步解說:在 WPF 應用程式中使用 ReportViewer 显示 rdlc
查看>>
ASP.NET应用程序与页面生命周期
查看>>
94. 二叉树的中序遍历
查看>>
Django之用户认证—auth模块
查看>>
单链表的拆分
查看>>
jQuery 事件
查看>>
(五)、装饰模式
查看>>
线程的工作方式-流水线
查看>>
git的安装
查看>>
[Luogu 3398] 仓鼠找sugar
查看>>
Vue用router.push(传参)跳转页面,参数改变,跳转页面数据不刷新的解决办法...
查看>>
jdk 的一些性能工具(jvm读书笔记)
查看>>
泛型方法、泛型接口、泛型代理、泛型类别测试
查看>>
Linux(Ubuntu) 下 SecureCRT 7 30天循环破解
查看>>
Day-23 基础模块4 模块导入_包
查看>>
第2节 mapreduce深入学习:7、MapReduce的规约过程combiner
查看>>
[改善Java代码]警惕泛型是不能协变和逆变的
查看>>
Elasticsearch使用filter进行匹配关系and,or,not,range查询
查看>>
浏览器被恶心页面占用
查看>>