为什么C++编译器不能支持对模板的分离式编译.docxVIP

为什么C++编译器不能支持对模板的分离式编译.docx

  1. 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
  2. 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载
  3. 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
  4. 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
  5. 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们
  6. 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
  7. 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
为什么 C++编译器不能支持对模板的分离式编译 首先,C++标准中提到,一个编译单元[translation unit]是指一个.cpp 文件以及它所 include 的所有.h 文件,.h 文件里的代码将会被扩展到包含它的.cpp 文件里,然后编译器编译该.cpp 文件为一个.obj 文件,后者拥有 PE[Portable Executable,即 windows 可执行文件]文件格式, 并且本身包含的就已经是二进制码,但是,不一定能够执行,因为并不保证其中一定有 main 函数。当编译器将一个工程里的所有.cpp 文件以分离的方式编译完毕后,再由连接器(linker) 进行连接成为一个.exe 文件。 举个例子: //---------------test.h // void f();//这里声明一个函数 f //---------------test.cpp // #include”test.h” void f() { …//do something } //这里实现出 test.h 中声明的f 函数 //---------------main.cpp // #include”test.h” int main() { f(); //调用 f,f 具有外部连接类型 } 在这个例子中,test. cpp 和 main.cpp 各被编译成为不同的.obj 文件[姑且命名为 test.obj 和main.obj],在 main.cpp 中,调用了 f 函数,然而当编译器编译 main.cpp 时,它所仅仅知道的只是 main.cpp 中所包含的 test.h 文件中的一个关于 void f();的声明,所以,编译器将这里 的f 看作外部连接类型,即认为它的函数实现代码在另一个.obj 文件中,本例也就是test.obj, 也就是说,main.obj 中实际没有关于f 函数的哪怕一行二进制代码,而这些代码实际存在于test.cpp 所编译成的 test.obj 中。在 main.obj 中对f 的调用只会生成一行 call 指令,像这样: call f [C++中这个名字当然是经过 mangling[处理]过的] 在编译时,这个call 指令显然是错误的,因为main.obj 中并无一行f 的实现代码。那怎么办呢?这就是连接器的任务,连接器负责在其它的.obj 中[本例为 test.obj]寻找 f 的实现代码, 找到以后将 call f 这个指令的调用地址换成实际的f 的函数进入点地址。需要注意的是:连接器实际上将工程里的.obj“连接”成了一个.exe 文件,而它最关键的任务就是上面说的, 寻找一个外部连接符号在另一个.obj 中的地址,然后替换原来的“虚假”地址。 这个过程如果说的更深入就是: call f 这行指令其实并不是这样的,它实际上是所谓的stub,也就是一个 jmp 0x23423[这个地址可能是任意的,然而关键是这个地址上有一行指令来进行真正的 call f 动作。也就是说,这个.obj 文件里面所有对f 的调用都 jmp 向同一个地址,在后者那儿才真正”call”f。这样做的好处就是连接器修改地址时只要对后者的call XXX 地址作改动就行了。但是,连接器是如何找到f 的实际地址的呢[在本例中这处于 test.obj 中],因为.obj 于.exe 的格式都是一样的,在这样的文件中有一个符号导入表和符号导出表[import table 和export table]其中将所有符号和它们的地址关联起来。这样连接器只要在 test.obj 的符号导出表中寻找符号 f[当然 C++对 f 作了 mangling]的地址就行了,然后作一些偏移量处理后[因为是将两个.obj 文件合并,当然地址会有一定的偏移,这个连接器清楚]写入 main.obj 中的符号导入表中f 所占有的那一项。 这就是大概的过程。其中关键就是: 编译 main.cpp 时,编译器不知道 f 的实现,所有当碰到对它的调用时只是给出一个指示,指示连接器应该为它寻找f 的实现体。这也就是说main.obj 中没有关于f 的任何一行二进制代码。 编译 test.cpp 时,编译器找到了f 的实现。于是乎f 的实现[二进制代码]出现在 test.obj 里。 连接时,连接器在test.obj 中找到f 的实现代码[二进制]的地址[通过符号导出表]。然后将 main.obj 中悬而未决的 call XXX 地址改成f 实际的地址。 完成。 然而,对于模板,你知道,模板函数的代码其实并不能直接编译成二进制代码,其中要有一个“具现化”的过程。举个例子: //----------main.cpp // templateclass T void f(T t)

文档评论(0)

hao187 + 关注
官方认证
文档贡献者

该用户很懒,什么也没介绍

认证主体武汉豪锦宏商务信息咨询服务有限公司
IP属地上海
统一社会信用代码/组织机构代码
91420100MA4F3KHG8Q

1亿VIP精品文档

相关文档