看过C++Primer中的表述之后,进行了各种实验,但总是不能自圆其说,于是进行了一番调查,本文采用的是另一种说法。
实例说明
为了更好的理解问题,先看实例。首先是主角。
模板类
模板的内容分别保存在.h文件和.cpp文件中。
FirstTester
模板的第一个利用者,这里只表示.cpp的内容。
本类中两次实例化了TemplateClass模板,分别生成tf和tf1两个对象。
SecondTester
模板的第二个利用者,同样只表示是.cpp的内容。
本类中两次实例化了TemplateClass模板,分别生成ts和ts1两个对象。
main函数
FirstTester,SecondTester的利用者,同时本身也会利用模板。
本类中两次实例化了TemplateClass模板,分别生成t2和t3两个对象。
问题来了
上面的代码经过编译之后,会存在多少个TemplateClass的实例类呢?答案是只有一个。按照作者的理解,其原因至少有两个:
-
如果实例化出来的是不同的类,那么生成的各个实例的类型就会各不相同,这不符合程序员的本意。
-
浪费空间。
将多次模板类实例化压缩成一次,这个处理是在编译和链接过程中实现的。目前主要有两种方式。
Borland模式
每个文件独立编译,遇到模板定义和实例化都直接编译。在链接的时候将所有目标文件中的模板定义和示例化都收集起来,根据需要只保留一个。这种方法的优点是实现简单,缺点是浪费时间和空间。
Cfront模式
每个文件编译时,如果遇到模板定义和实例化都不直接编译,而是存储在另外的地方。在链接时再根据实际需要编译,生成代码并装配。这种方法的优点是效率高,缺点的实现复杂。
C++11的解决方式
出现这种复杂问题的主要原因就是编译器无法预知那次实例化是需要最后保留的。为了解决这个问题,C++11中增加了一个新特性,可以让程序员按照以下方式显式实例化模板:
其他的地方,则明确表明使用其他某个地方实例化的模板类。
已经说的这么明白了,留给编译器的工作就简单多了。
参考资料
Where’s the Template?
https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html
如果觉得文章对您有帮助,欢迎点赞并推荐给您的朋友!
阅读更多更新文章,请扫描下面二维码,关注微信公众号【面向对象思考】