c++模板类(一)理解编译器的编译模板过程

上传人:re****.1 文档编号:557779302 上传时间:2022-11-22 格式:DOCX 页数:12 大小:23.08KB
返回 下载 相关 举报
c++模板类(一)理解编译器的编译模板过程_第1页
第1页 / 共12页
c++模板类(一)理解编译器的编译模板过程_第2页
第2页 / 共12页
c++模板类(一)理解编译器的编译模板过程_第3页
第3页 / 共12页
c++模板类(一)理解编译器的编译模板过程_第4页
第4页 / 共12页
c++模板类(一)理解编译器的编译模板过程_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《c++模板类(一)理解编译器的编译模板过程》由会员分享,可在线阅读,更多相关《c++模板类(一)理解编译器的编译模板过程(12页珍藏版)》请在金锄头文库上搜索。

1、C+模板类(一)理解编译器的编译模板过程分类:C+模板类2008-11-05 17:40 2734人阅读评论(4) 收藏举报如何组织编写模板程序刖言常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编 写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL,以及Boost的 模板类,都能体会到这样的滋味:接口简单,操作复杂。我在5年前开始使用模板,那时我看到了 MFC的容器类。直到去年我还没有必要自己编写 模板类。可是在我需要自己编写模板类时,我首先遇到的事实却是传统”编程方法(在*.h文 件声明,在*.cpp文件中定义)不能用于模板。于是我

2、花费一些时间来了解问题所在及其解决 方法。本文对象是那些熟悉模板但还没有很多编写模板经验的程序员。本文只涉及模板类,未涉及 模板函数。但论述的原则对于二者是一样的。问题的产生通过下例来说明问题。例如在array.h文件中有模板类array:/ array.htemplate vtype name T, int SIZEclass arrayT data_SIZE;array (const array& other);const array& operator = (const array& other);public:array();T& operator( int i) retur n da

3、ta;const T& get_elem (int i) const retur n data;void set_elem(i nt i, const T& value) data_i = value;operator T*() retur n data_;然后在main.cpp文件中的主函数中使用上述模板:/ ma in .cpp#i nclude array.hint main (void)array vint, 50 in tArray;in tArray.set_elem(O, 2);in t firstElem = in tArray.get_elem(0);int* beg in

4、= in tArray;这时编译和运行都是正常的。程序先创建一个含有50个整数的数组,然后设置数组的第一 个元素值为2,再读取第一个元素值,最后将指针指向数组起点。但如果用传统编程方式来编写会发生什么事呢?我们来看看:将array.h文件分裂成为array.h和array.cpp二个文件(main.cpp保持不变)/ array.htemplate :operator (i nt i)return data_i;templatevtype name T, int SIZE const T& arrayvT, SIZE:get_elem(i nt i) constreturn data_i;te

5、mplatevtype name T, int SIZE void arrayvT, SIZE:set_elem(i nt i, const T& value)data_i = value;templatevtype name T, i nt SIZE arrayvT, SIZE:operator T*()return data_;编译时会出现3个错误。问题出来了:为什么错误都出现在第一个地方?为什么只有3个链接出错? array.cpp中有4个成员函数。要回答上面的问题,就要深入了解模板的实例化过程。模板实例化程序员在使用模板类时最常犯的错误是将模板类视为某种数据类型。所谓类型参量化(par

6、ameterized types)这样的术语导致了这种误解。模板当然不是数据类型,模板就是模板, 恰如其名:编译器使用模板,通过更换模板参数来创建数据类型。这个过程就是模板实例化(In sta ntiati on)。从模板类创建得到的类型称之为特例(specializatio n)。模板实例化取决于编译器能够找到可用代码来创建特例(称之为实例化要素,point of in sta ntiati on)。要创建特例,编译器不但要看到模板的声明,还要看到模板的定义。模板实例化过程是迟钝的,即只能用函数的定义来实现实例化。再回头看上面的例子,可以知道array是一个模板,arrayvint, 50是

7、一个模板实例-一个 类型。从array创建arrayvi nt, 50的过程就是实例化过程。实例化要素体现在ma in .cpp文 件中。如果按照传统方式,编译器在array.h文件中看到了模板的声明,但没有模板的定义, 这样编译器就不能创建类型arrayvint, 50。但这时并不出错,因为编译器认为模板定义在 其它文件中,就把问题留给链接程序处理。现在,编译array.cpp时会发生什么问题呢?编译器可以解析模板定义并检查语法,但不能 生成成员函数的代码。它无法生成代码,因为要生成代码,需要知道模板参数,即需要一个 类型,而不是模板本身。这样,链接程序在main.cpp或array.cpp

8、中都找不到arrayvint, 50的定义,于是报出无 定义成员的错误。至此,我们回答了第一个问题。但还有第二个问题,在array.cpp中有4个成员函数,链接 器为什么只报了 3个错误?回答是:实例化的惰性导致这种现象。在ma in .cpp中还没有用 上operator,编译器还没有实例化它的定义。解决方法认识了问题,就能够解决问题:在实例化要素中让编译器看到模板定义。用另外的文件来显式地实例化类型,这样链接器就能看到该类型。使用export关键字。前二种方法通常称为包含模式,第三种方法则称为分离模式。第一种方法意味着在使用模板的转换文件中不但要包含模板声明文件,还要包含模板定义文 件。在

9、上例中,就是第一个示例,在array.h中用行内函数定义了所有的成员函数。或者在 main.cpp文件中也包含进array.cpp文件。这样编译器就能看到模板的声明和定义,并由此 生成arrayvi nt, 50实例。这样做的缺点是编译文件会变得很大,显然要降低编译和链接速 度。第二种方法,通过显式的模板实例化得到类型。最好将所有的显式实例化过程安放在另外的 文件中。在本例中,可以创建一个新文件templatei nsta ntiatio ns.cpp:/ template in sta ntiati on s.cpp#in elude array.cpp template class arr

10、ay vint, 50; / 显式实例化array vint, 50 类型不是在 main, cpp 中产生,而是在 template in sta ntiati on s.cpp 中产生。这 样链接器就能够找到它的定义。用这种方法,不会产生巨大的头文件,加快编译速度。而且 头文件本身也显得更加“干净”和更具有可读性。但这个方法不能得到惰性实例化的好处,即 它将显式地生成所有的成员函数。另外还要维护templatei nsta ntiatio ns.cpp文件。第三种方法是在模板定义中使用export关键字,剩下的事就让编译器去自行处理了。当我 在Stroustrup的书中读到export时,

11、感到非常兴奋。但很快就发现VC 6.0不支持它,后来又 发现根本没有编译器能够支持这个关键字(第一个支持它的编译器要在2002年底才问世)。自那以后,我阅读了不少关于export的文章,了解到它几乎不能解决用包含模式能够解决 的问题。欲知更多的export关键字,建议读读Herb Sutter撰写的文章。结论要开发模板库,就要知道模板类不是所谓的原始类型”,要用其它的编程思路。本文目的不 是要吓唬那些想进行模板编程的程序员。恰恰相反,是要提醒他们避免犯下开始模板编程时 都会出现的错误。/http:/ ng/archive/2004/11/12/63139.aspx甚至是在定义非内联函数时,模板

12、的头文件中也会放置所有的声明和定义。这似乎违背了通 常的头文件规则:“不要在分配存储空间前放置任何东西”,这条规则是为了防止在连接时的 多重定义错误。但模板定义很特殊。由templatev.处理的任何东西都意味着编译器在当 时不为它分配存储空间,它一直出于等待状态直到被一个模板实例告知。在编译器和连接器 的某一处,有一机制能去掉模板的多重定义,所以为了容易使用,几乎总是在头文件中放置 全部的模板声明和定义。为什么C+编译器不能支持对模板的分离式编译刘未鹏(po ngba) /文首先,C+标准中提到,一个编译单元tra nslation unit是指一个.cpp文件以及它所in elude 的所

13、有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp 文件为一个.obj文件,后者拥有PEPortable Executable,即windows可执行文件文件格式, 并且本身包含的就已经是二进制码,但是,不一定能够执行,因为并不保证其中一定有main 函数。当编译器将一个工程里的所有.cpp文件以分离的方式编译完毕后,再由连接器(li nker) 进行连接成为一个.exe文件。举个例子:/test.h/void f();这里声明一个函数f/test.cpp/#i ncludetest.h”void f()./do someth ing 这里实现出test.

14、h中声明的f函数/main.cpp/#i ncludetest.h”int ma in()f(); 调用f,f具有外部连接类型在这个例子中,test. cpp和main.cpp各被编译成为不同的.obj文件姑且命名为test.obj和 ma in .obj,在ma in .cpp中,调用了 f函数,然而当编译器编译mai n.cpp时,它所仅仅知道 的只是ma in .cpp中所包含的test.h文件中的一个关于void f();的声明,所以,编译器将这 里的f看作外部连接类型,即认为它的函数实现代码在另一个.obj文件中,本例也就是 test.obj,也就是说,main.obj中实际没有关于f函数的哪怕一行二进制代码,而这些代码实 际存在于test.cpp所编译成的test.obj中。在main.obj中对f的调用只会生成一行c

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 学术论文 > 其它学术论文

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号