C++ 泛型编程基础:模板通识

发表于:2016-11-17 09:44

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Fireplusplus    来源:51Testing软件测试网采编

  测试环境:Target: x86_64-linux-gnu
  gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2.1)
  什么是泛型编程?为什么C++会有模板?这一切的一切都要从如何编写一个通用的加法函数说起。
  很久很久以前
  有一个人要编写一个通用的加法函数,他想到了这么几种方法:
  使用函数重载,针对每个所需相同行为的不同类型重新实现它
  int Add(const int &_iLeft, const int &_iRight)
  {
  return (_iLeft + _iRight);
  }
  float Add(const float &_fLeft, const float &_fRight)
  {
  return (_fLeft + _fRight);
  }
  当然不可避免的有自己的缺点:
  1、只要有新类型出现,就要重新添加对应函数,太麻烦
  2、代码的复用率低
  3、如果函数只是返回值类型不同,函数重载不能解决(函数重载的条件:同一作用域,函数名相同,参数列表不同)
  4、一个方法有问题,所有的方法都有问题,不好维护
  · 使用公共基类,将通用的代码放在公共的基础类里面
  【缺点】
  1、借助公共基类来编写通用代码,将失去类型检查的优点
  2、对于以后实现的许多类,都必须继承自某个特定的基类,代码维护更加困难
  
  #define ADD(a, b) ((a) + (b))
  1、不进行参数类型检测,安全性不高
  2、编译预处理阶段完成替换,调试不便
  所以在C++中又引入了泛型编程的概念。泛型编程是编写与类型无关的代码。这是代码复用的一种手段。模板则是泛型编程的基础。
  模板分为了函数模板和类模板:
  函数模板
  函数模板:代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
  什么意思呢?往下看就知道了。
  模板函数的格式
  template <typename T> //T可是自己起的名字,满足命名规范即可
  T Add(T left, T right)
  {
  return left + right;
  }
  int main()
  {
  Add(1, 2); //right 调用此函数是将
  Add(1, 2.0); //error 只有一个类型T,传递两个不同类型参数则无法确定模板参数T的类型,编译报错
  return 0;
  }
  对第一个函数调用,编译器生成了 int Add(int, int) 这样一个函数。
  typename是用来定义模板参数关键字,也可以使用class。不过建议还是尽量使用typename,因为这个关键字是为模板而生的!
  注意:不能使用struct代替typename。(这里的class不是之前那个class的意思了,所以你懂的)
  当然你也可以把函数模板声明为内联的:
  template <typename T>
  inline T Add(T left, T right) {//…}
  实例化
  编译器用模板产生指定的类或者函数的特定类型版本,产生模板特定类型的过程称为函数模板实例化。(用类类型来创建一个对象也叫做实例化哦!)
  模板的编译
  模板被编译了两次:
  1、实例化之前,检查模板代码本身,查看是否出现语法错误,如:遗漏分号(遗憾的是不一定能给检查的出来)
  2、在实例化期间,检查模板代码,查看是否所有的调用都有效,如:实例化类型不支持某些函数调用
  实参推演
  从函数实参确定模板形参类型和值的过程称为模板实参推演。多个类型形参的实参必须完全匹配。
  如对这样的函数调用:
  template <typename T1, typename T2, typename T3>
  void fun(T1 t1, T2 t2, T3 t3)
  {
  //do something
  }
  int main()
  {
  fun(1, 'a', 3.14);
  return 0;
  }
  编译器生成了如下这样的函数:
  其中,函数参数的类型是和调用函数传递的类型完全匹配的。
  类型形参转换
  一般不会转换实参以匹配已有的实例化,相反会产生新的实例。
  举个栗子:对如下的函数调用:
  template <typename T>
  T Add(T left, T right)
  {
  return left + right;
  }
  int Add(int left, int right)
  {
  return left + right;
  }
  int main()
  {
  Add(1.2, 3.4);
  return 0;
  }
  即程序中已经实现过了Add函数的int版本,那么调用Add(1.2, 3.4);时,编译器不会将1.2和3.4隐式转换为int型,进而调用已有的Add版本,而是重新合成一个double的版本:
  当然前提是能够生成这么一个模板函数。如果这个模板函数无法生成的话,那么只能调用已有的版本了。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号