那天,风和日丽。 于是,我开始了模板类的学习。 还记得,写的是模板类的顺序表和双向链表。 但是,编好程之后,程序编译能通过,但是却无法运行。
链接错误。额,那就是间接表明并不是代码出现了什么错误。 那那那那,问题出在哪里呢?
那就是 。 。 。 。 。
那么,在Test.obj中实际上是没有关于所调用函数的二进制代码的,这些代码实际存在于SeqList.obj中。在Test.obj中对函数的调用只会生成相应的call指令。
例如:call f [C++中这个名字当然是经过mangling[处理]过的]
在编译时,这样的call指令实际上是错误的,因为在生成的.obj文件中并没有关于函数实现的代码。
这个时候,连接器便出现了。连接器会在其它的obj文件中查找所调用函数的实现代码,找到之后将call指令里的函数调用地址换成实际函数进入点的地址。值得一提的是,连接器实际上将工程里的obj文件生成了一个exe文件,而它最最最关键的任务就是,寻找一个外部连接符号在另外的obj文件中的地址,然后替换原来的虚假地址。更深入说的话,call指令实际上是stub,即jmp 0xABCDEF。地址是任意的,关键是这个地址上有一行真正的指令来进行call函数的动作。即,这个obj文件中所以对函数的调用最终都会jmp到同一个地址,在那儿,才会进行真正函数的调用。这样做的好处是,连接器修改地址时只要对真正调用函数的地方进行改动。那连接器是如何找到函数的实际地址的呢?因为obj和exe的格式实际上是一样的,而在这样的文件中有一个符号导入表和符号导出表。其中,会将所有的符号和他们的地址关联起来。这样连接器只要在SeqList.obj的符号导出表中查找符好函数的地址就行了。在做一些偏移量处理之后,写入Test.obj符号导入表中调用函数所占有的那部分即可。这样Test.obj中也就有了函数的二进制代码段。以此类推。
然而实例化要求编译器知道模板的定义,不是吗?
在分离式编译的环境下,编译器编译某一个.cpp文件时并不知道另一个.cpp文件的存在,也不会去查找(当遇到未决符号时它会寄希望于连接器)。这种模式在没有模板的情况下运行良好,但遇到模板时就傻眼了,因为模板仅在需要的时候才会实例化出来。所以,当编译器只看到模板的声明时,它不能实例化该模板,只能创建一个具有外部连接的符号并期待连接器能够将符号的地址决议出来。然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码,于是连接器也黔驴技穷了。所以便会出现链接错误啦~那遇到这样的错误我们该怎么处理呢?1、在模板头文件 xxx.h 里面显示实例化->模板类的定义后面添加 template class SeqList; 一般不推荐这种方法, 一方面老编译器可能不支持,另一方面实例化依赖调用者。(不推荐)2、 将声明和定义放到一个文件 “xxx.hpp” 里面,推荐!
参考博客:http://blog.csdn.net/K346K346/article/details/48879021 参考博客:http://blog.csdn.net/pongba/article/details/19130
大概是在六月吧,和一位老师还有几位同学于夏日的深夜一起回宿舍。那时的西安已经十分燥热了,蝉在树上叫啊叫啊,在疲累十分的夜晚实在十分烦人了。 我们一行人本来一路无言,此时老师突然打破静寂说,“夏天的夜晚啊,就应该站在有蝉鸣的树下谈情说爱。” 听罢这句话,大家一齐笑起来。 老师大概是毕业还没几年,心中还保有了罗曼蒂克的情怀。 近现代的翻译实在拗口,但读起来却又动人的可爱,比起romantic来,罗曼蒂克不知怎得生生多出几丝情。 月光和灯光交相辉映之下,已经微有些发福的老师硬是散发出罗曼蒂克的劲儿来。 当时我想,啊真好,还没有在日复一日的学生工作中丧失自己的本心,还能有如此的情怀,可爱极了。 老师总是很幽默的把自己身上发生的一切堪称操蛋至极的事笑话一般的说出来,老师也说啊,人呢在青春尚在的时候,要不计代价的成长。 这位老师,可谓是把中年的自己活成了少年模样啊哈哈。 人呢,不怕老去。怕的是,白白老去了。 嗯,写到这里,听着窗外的蝉鸣,也算是有不那么差劲的罗曼蒂克了。 忘记什么也不能忘了生活,把自己活成花生人一样,那可太没意思~ 夏日的晚上,要听成诗京的歌。一位来自韩国的抒情歌手,出道二十多年了吧,以温柔的声线著称。 当然也是因为我的歌单里面,只有他还没被完全和谐掉了。 以上,晚安啦~
