博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【转】模板类实例化的相关常识
阅读量:6182 次
发布时间:2019-06-21

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

hot3.png

模板函数在声明的时候, 其实并不存在,函数地址也就无从谈起了,而导出到动态链接库的函数都需要有地址(动态链接库不能将模板类导出,因为没法生成实例)如果把模板类的声明和定义都放在头文件中。需要用到模板类的时候,只需要包含该头文件,然后进行实例化。如果模板类的声明和定义分别放在头文件和源文件中。当实例化的时候,只包含头文件会发证链接错误。原因是模板类的实例化要分成两个步骤,模板的特例化和特例的实力化。编译器在编译阶段,由于没有定义,所以编译器不能生成具体的模板特例,但是这并不会报错误,编译器将把问题抛给链接器来做。在编译源文件的时候,程序找不到该模板的特例,只是有模板而已,所以无法生成对象。所以会发生错误。以下转自牛人blog=================前言  常遇到询问使用模板到底是否容易的问题,我的回答是:“模板的使用是容易的,但组织编写却不容易”。看看我们几乎每天都能遇到的模板类吧,如STL, ATL, WTL, 以及Boost的模板类,都能体会到这样的滋味:接口简单,操作复杂。  本文对象是那些熟悉模板但还没有很多编写模板经验的程序员。本文只涉及模板类,未涉及模板函数。但论述的原则对于二者是一样的。  问题的产生  通过下例来说明问题。例如在array.h文件中有模板类array:  // array.h  template 
  class array  {  T data_[SIZE];  array (const array& other);  const array& operator = (const array& other);  public:  array(){};  T& operator[](int i) {return data_[i];}  const T& get_elem (int i) const {return data_[i];}  void set_elem(int i, const T& value) {data_[i] = value;}  operator T*() {return data_;}  };  然后在main.cpp文件中的主函数中使用上述模板:  // main.cpp  #include "array.h"  int main(void)  {  array
intArray;  intArray.set_elem(0, 2);  int firstElem = intArray.get_elem(0);  int* begin = intArray;  }  这时编译和运行都是正常的。程序先创建一个含有50个整数的数组,然后设置数组的第一个元素值为2,再读取第一个元素值,最后将指针指向数组起点。  但如果用传统编程方式来编写会发生什么事呢?我们来看看:  将array.h文件分裂成为array.h和array.cpp二个文件(main.cpp保持不变)  // array.h  template
  class array  {  T data_[SIZE];  array (const array& other);  const array& operator = (const array& other);  public:  array(){};  T& operator[](int i);  const T& get_elem (int i) const;  void set_elem(int i, const T& value);  operator T*();  }; // array.cpp  #include "array.h"  template
T& array
::operator [](int i)  {  return data_[i];  }  template
const T& array
::get_elem(int i) const  {  return data_[i];  }  template
void array
::set_elem(int i, const T& value)  {  data_[i] = value;  }  template
array
::operator T*()  {  return data_;  }  编译时会出现3个错误。问题出来了:  为什么错误都出现在第一个地方?  为什么只有3个链接出错?array.cpp中有4个成员函数。  要回答上面的问题,就要深入了解模板的实例化过程。  模板实例化  程序员在使用模板类时最常犯的错误是将模板类视为某种数据类型。所谓类型参量化(parameterized types)这样的术语导致了这种误解。模板当然不是数据类型,模板就是模板,恰如其名:  编译器使用模板,通过更换模板参数来创建数据类型。这个过程就是模板实例化(Instantiation)。  从模板类创建得到的类型称之为特例(specialization)。  模板实例化取决于编译器能够找到可用代码来创建特例(称之为实例化要素,  point of instantiation)。  要创建特例,编译器不但要看到模板的声明,还要看到模板的定义。  模板实例化过程是迟钝的,即只能用函数的定义来实现实例化。  再回头看上面的例子,可以知道array是一个模板,array
是一个模板实例 - 一个类型。从array创建array
的过程就是实例化过程。实例化要素体现在main.cpp文件中。如果按照传统方式,编译器在array.h文件中看到了模板的声明,但没有模板的定义,这样编译器就不能创建类型array
。但这时并不出错,因为编译器认为模板定义在其它文件中,就把问题留给链接程序处理。  现在,编译array.cpp时会发生什么问题呢?编译器可以解析模板定义并检查语法,但不能生成成员函数的代码。它无法生成代码,因为要生成代码,需要知道模板参数,即需要一个类型,而不是模板本身。  这样,链接程序在main.cpp 或 array.cpp中都找不到array
的定义,于是报出无定义成员的错误。  至此,我们回答了第一个问题。但还有第二个问题,在array.cpp中有4个成员函数,链接器为什么只报了3个错误?回答是:实例化的惰性导致这种现象。在main.cpp中还没有用上operator[],编译器还没有实例化它的定义。  解决方法  认识了问题,就能够解决问题:  在实例化要素中让编译器看到模板定义。  用另外的文件来显式地实例化类型,这样链接器就能看到该类型。  使用export关键字。  前二种方法通常称为包含模式,第三种方法则称为分离模式。  第一种方法意味着在使用模板的转换文件中不但要包含模板声明文件,还要包含模板定义文件。在上例中,就是第一个示例,在array.h中用行内函数定义了所有的成员函数。或者在main.cpp文件中也包含进array.cpp文件。这样编译器就能看到模板的声明和定义,并由此生成array
实例。这样做的缺点是编译文件会变得很大,显然要降低编译和链接速度。  第二种方法,通过显式的模板实例化得到类型。最好将所有的显式实例化过程安放在另外的文件中。在本例中,可以创建一个新文件templateinstantiations.cpp:  // templateinstantiations.cpp  #include "array.cpp"  template class array
; // 显式实例化  array
类型不是在main.cpp中产生,而是在templateinstantiations.cpp中产生。这样链接器就能够找到它的定义。用这种方法,不会产生巨大的头文件,加快编译速度。而且头文件本身也显得更加“干净”和更具有可读性。但这个方法不能得到惰性实例化的好处,即它将显式地生成所有的成员函数。另外还要维护templateinstantiations.cpp文件。  第三种方法是在模板定义中使用export关键字,剩下的事就让编译器去自行处理了。当我在  Stroustrup的书中读到export时,感到非常兴奋。但很快就发现VC 6.0不支持它,后来又发现根本没有编译器能够支持这个关键字(第一个支持它的编译器要在2002年底才问世)。自那以后,我阅读了不少关于export的文章,了解到它几乎不能解决用包含模式能够解决的问题。欲知更多的export关键字,建议读读Herb Sutter撰写的文章。  结论  要开发模板库,就要知道模板类不是所谓的"原始类型",要用其它的编程思路。本文目的不是要吓唬那些想进行模板编程的程序员。恰恰相反,是要提醒他们避免犯下开始模板编程时都会出现的错误。

转载于:https://my.oschina.net/ITHaozi/blog/282873

你可能感兴趣的文章
Hadoop2源码分析-YARN RPC 示例介绍
查看>>
关于AI,那些年“砖家”曾经发表的所谓科技预测
查看>>
航天科工自行研发“反无人机”系统,综合拦截成功率高达80%
查看>>
中科曙光与寒武纪合作推出AI服务器,将实时分析能力提升百倍
查看>>
IBM押注AI、量子计算、区块链,发布未来5年五大科技预测
查看>>
风口行业程序员必看!直播网络成本降低70%,智能接入网关是怎样实现的?
查看>>
2018第五届中国机器人应用与产业发展论坛 &2018中国智能包装工业发展大会
查看>>
日本研发投篮机器人Cue,投球命中率接近100%
查看>>
WPS for Linux字体配置(Ubuntu 16.04)
查看>>
陈妍希和你一起带“蛙儿子”做公益,守护宝贝她有话跟你说!
查看>>
享受生活:值得关注的七件家居智能硬件
查看>>
一个工具箱工具
查看>>
ASP.NET深入浅出系列2-页面生命周期详解
查看>>
暴风魔镜合伙人崔海庆演讲实录:用VR社交来撼动世界
查看>>
补全等式(DFS,全排列)
查看>>
如何编写linux下nand flash驱动-1
查看>>
异步社区本周半价电子书
查看>>
2星|《自金融》:公开信息汇总,缺乏深度,缺乏自有观点
查看>>
inode节点与硬链接概念详解
查看>>
OSX下配置gradle
查看>>