模板与泛型编程

--模板定义

引言:

所谓泛型程序就是以独立于不论什么特定类型的方式编写代码。使用泛型程序时,我们须要提供详细程序实例所操作的类型或值。

模板是泛型编程的基础。使用模板时能够无须了解模板的定义

泛型编程与面向对象编程一样,都依赖于某种形式的多态性。面向对象编程中的多态性在执行时应用于存在继承关系的类。我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异。仅仅要使用基类的引用或指针,基类类型或派生类类型的对象就能够使用同样的代码。

在泛型编程中,我们所编写的类和函数能够多态地用于跨越编译时不相关的类型。一个类或一个函数能够用来操纵多种类型对象。标准库中的容器、迭代器和算法是非常好的泛型编程的样例。标准库用独立于类型的方式定义每一个容器、迭代器和算法,因此差点儿能够在随意类型上使用标准库的类和函数

在C++中,模板是泛型编程的基础。模板是创建类或函数的蓝图或公式。

编写重载函数:

  1. int compare(const string &v1,const string &v2)
  2. {
  3. if (v1 < v2)
  4. {
  5. return -1;
  6. }
  7. else if (v1 > v2)
  8. {
  9. return 1;
  10. }
  11.  
  12. return 0;
  13. }
  14.  
  15. int compare(const double &v1,const double &v2)
  16. {
  17. if (v1 < v2)
  18. {
  19. return -1;
  20. }
  21. else if (v1 > v2)
  22. {
  23. return 1;
  24. }
  25.  
  26. return 0;
  27. }

这些函数差点儿同样,它们之间唯一的差别是形參的类型,每一个函数的函数体是同样的

每一个要比較的类型都须要反复函数的函数体,不仅麻烦并且easy出错。更重要的是,须要事先知道空间可能会比較哪些类型。假设希望将函数用于未知类型,这样的策略就不起作用了

一、定义函数模板

我们能够不用为每一个类型定义一个新函数,而是定义一个函数模板。函数模板是一个独立于类型的函数,能够作为一种方式,产生函数的特定类型版本号。

  1. template<typename T>
  2. int compare(const T &v1,const T &v2)
  3. {
  4. if (v1 < v2)
  5. {
  6. return -1;
  7. }
  8. else if (v1 > v2)
  9. {
  10. return 1;
  11. }
  12.  
  13. return 0;
  14. }

模板定义以keywordtemplate開始,后接模板形參表,模板形參表是用尖括号括住的一个或多个模板形參的列表,形參之间以逗号分隔。并且模板形參表不能为空。

1、模板形參表

模板形參表类似于函数形參表,表示能够在类或函数的定义中使用的类型或值。比如,compare 函数声明一个名为T的类型形參。在compare内部,能够使用名字T引用一个类型,T表示哪个实际类型由编译器依据所用的函数而确定

模板形參能够是表示类型的类型形參,或者是表示常量表达式的非类型形參。类型形參跟在keywordclasstypename之后定义。

2、使用函数模板

使用函数模板时,编译器会判断哪个(或哪些)模板实參绑定到模板形參。一旦编译器确定了实际的模板实參,就称它实例化了函数模板的一个实例。

推导出实际模板实參后,编译器使用实參取代相应的模板形參产生编译该版本号的函数。

  1. int main ()
  2. {
  3. // 绑定到compare(const int&, const int&)
  4. cout << compare(1, 0) << endl;
  5.  
  6. // 绑定到compare(const string&, const string&)
  7. string s1 = "hi", s2 = "world";
  8. cout << compare(s1, s2) << endl;
  9. return 0;
  10. }

3、inline函数模板

inline说明符放在模板形參表之后、返回类型之前,不能放在keywordtemplate之前。

  1. template<typename T> inline
  2. int compare(const T &v1,const T &v2); //OK
  3.  
  4. inline template<typename T>
  5. int compare(const T &v1,const T &v2); //Error

  1. //P528 习题16.1
  2. template<typename T> inline
  3. T abs(T val)
  4. {
  5. return val > 0 ? val : -val;
  6. }
  7.  
  8. int main ()
  9. {
  10. cout << abs(-1) << endl;
  11. cout << abs(-0.98) << endl;
  12. cout << abs(short(3.4)) << endl;
  13. return 0;
  14. }

  1. //习题16.2
  2. template<typename T>
  3. ostream &write(ostream &os,T val)
  4. {
  5. return os << val << endl;
  6. }
  7.  
  8. int main()
  9. {
  10. write(cout,1);
  11. write(cout,12.3);
  12. write(cout,"Hello World");
  13.  
  14. ofstream outFile("output");
  15. write(outFile,"Hello");
  16. write(outFile,123);
  17.  
  18. string temp;
  19. ostringstream strStream(temp);
  20. write(strStream,"Hello_World");
  21.  
  22. cout << strStream.str() << endl;
  23. }

二、定义类模板

为了举例说明类模板,我们将为标准库queue类实现一个自己的版本号。

我们自己定义的Queue类必须能够支持不同类型的对象,所以将它定义为类模板。Queue所能支持的操作:

1)push:在队尾加入一项

2)pop:从队头删除一项

3)front:返回队头的引用

4)empty:指出队列是否为空

  1. template<typename Type> class Queue
  2. {
  3. public:
  4. Type &front();
  5. const Type &front() const;
  6.  
  7. void push(const Type &);
  8. void pop();
  9. bool empty() const;
  10.  
  11. private:
  12. //...
  13. };

类模板也是模板,因此必须以keywordtemplate开头,后接模板形參表。

除了模板形參表外,类模板的定义看起来与随意其它类类似。在类和类成员的定义中,能够使用模板形參作为类型或值的占位符,在使用类时再提供那些类型或值。

使用类模板

与调用函数模板形成对照,使用类模板时,必须为模板形參显式指定实參

  1. Queue<int> qi;
  2. Queue< vector<double> > qc;
  3. Queue<string> qs;

编译器使用实參来实例化这个类的特定类型版本号。实质上,编译器用用户提供的实际特定类型取代Type,又一次编写Queue。在这个样例中,编译器将实例化三个Queue:第一个用int取代 Type,第二个用vector<double>取代 Type,第三个用string取代 Type。

  1. //P529 习题16.5
  2. template<typename T>
  3. T getBigger(const T &val1,const T &val2)
  4. {
  5. return val1 > val2 ? val1 : val2;
  6. }

  1. //习题16.6
  2. template<typename Type> class List
  3. {
  4. public:
  5. List();
  6.  
  7. void push_back(const Type &);
  8. void push_front(const Type &);
  9.  
  10. std::size_t size() const;
  11.  
  12. void insert(Type *ptr,const Type &val);
  13.  
  14. bool empty();
  15.  
  16. private:
  17. //...
  18. };

三、模板形參

像函数形參一样,为模板形參选择的名字没有本质含义:

  1. template<typename Glorp>
  2. int compare(const Glorp &v1,const Glorp &v2)
  3. {
  4. if (v1 < v2)
  5. {
  6. return -1;
  7. }
  8. else if (v1 > v2)
  9. {
  10. return 1;
  11. }
  12.  
  13. return 0;
  14. }

该代码与前面定义的compare模板一样。

能够给模板形參授予的唯一含义是差别是类型形參还是非类型形參。假设是类型形參,我们就知道该形參表示未知类型,假设是非类型形參,我们就知道它是一个未知值。

假设希望使用模板形參所表示的类型或值,能够使用与相应模板形參同样的名字。比如,compare函数中全部的Glorp引用将在该函数被实例化时确定为同一类型。

1、模板形參作用域

模板形參的名字能够在声明为模板形參之后直到模板声明定义的末尾处使用。

模板形參遵循常规名字屏蔽规则:

  1. typedef double T;
  2. template <class T>
  3. T calc(const T &a,const T &b)
  4. {
  5. //此处T为template形參表中的T,全局名字被屏蔽
  6. T tmp = a;
  7. //...
  8. return tmp;
  9. }

2、使用模板形參名字的限制

用作模板形參的名字不能在模板内部重用:

  1. template <class T>
  2. T calc(const T &a,const T &b)
  3. {
  4. typedef double T; //Error
  5.  
  6. T tmp = a;
  7. //...
  8. return tmp;
  9. }

这一限制还意味着模板形參的名字仅仅能在同一模板形參表中使用一次:

  1. template <class T,class T> T calc(const T &a,const T &b); //Error

正如能够重用函数形參名字一样,模板形參的名字也能在不同模板中重用:

  1. template <class T> T calc(const T &a,const T &b);
  2.  
  3. template <class T> int compare(const T &,const T&); //OK

3、模板声明

像其它随意函数或类一样,对于模板能够仅仅声明而不定义声明必须指出函数或类是一个模板:

  1. template <class T>
  2. int compare(const T &,const T&);

同一模板的声明和定义中,模板形參的名字不必同样:

  1. template <class T>
  2. T calc(const T &,const T &);
  3.  
  4. template <typename U>
  5. U calc(const U&,const U&);
  6.  
  7. template <class Type>
  8. Type calc(const Type &,const Type &);

每一个模板类型形參前面必须带上keywordclass或typename,每一个非类型形參前面必须带上类型名字,省略keyword或类型说明符是错误的:

  1. template<typename T,U>
  2. T calc(const T &,const U &); //Error
  3.  
  4. template<typename T,class U>
  5. T calc(const T &,const U &); //OK

  1. //P531 习题16.9
  2. template <typename Type,typename T>
  3. Type find(Type begin,Type end,const T &val)
  4. {
  5. while (begin != end)
  6. {
  7. if (*begin == val)
  8. {
  9. return begin;
  10. }
  11. ++ begin;
  12. }
  13.  
  14. return end;
  15. }
  16.  
  17. int main()
  18. {
  19. int ia[] = {01,1,1,999,2,3,2,34,4,3,4};
  20.  
  21. int *p;
  22. if ((p = find(ia,ia+sizeof(ia)/sizeof(*ia),999)) != ia + sizeof(ia)/sizeof(*ia))
  23. {
  24. cout << *p << endl;
  25. }
  26. else
  27. {
  28. cout << "Not Found!" << endl;
  29. }
  30.  
  31. vector<int> iVec(ia,ia + sizeof(ia)/sizeof(*ia));
  32. vector<int>::iterator iter;
  33. if ((iter = find(iVec.begin(),iVec.end(),888)) != iVec.end())
  34. {
  35. cout << *iter << endl;
  36. }
  37. else
  38. {
  39. cout << "Not Found!" << endl;
  40. }
  41.  
  42. ifstream inFile("input");
  43. vector<string> strVec;
  44. string val;
  45.  
  46. while (inFile >> val)
  47. {
  48. strVec.push_back(val);
  49. }
  50.  
  51. vector<string>::iterator it;
  52. if ((it = find(strVec.begin(),strVec.end(),"hello")) != strVec.end())
  53. {
  54. cout << *it << endl;
  55. }
  56. else
  57. {
  58. cout << "Not Found!" << endl;
  59. }
  60. }

C++ Primer 学习笔记_75_模板与泛型编程 --模板定义的更多相关文章

  1. C++ Primer 学习笔记_76_模板与泛型编程 --模板定义[续]

    模板与泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...

  2. C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

    模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一 ...

  3. C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

    模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...

  4. C++ Primer 学习笔记_76_模板和泛型编程 --模板定义[继续]

    模板和泛型编程 --模板定义[续] 四.模板类型形參 类型形參由keywordclass或 typename后接说明符构成.在模板形參表中,这两个keyword具有同样的含义,都指出后面所接的名字表示 ...

  5. C++ Primer 学习笔记_79_模板与泛型编程 --模板编译模型

    模板与泛型编程 --模板编译模型 引言: 当编译器看到模板定义的时候,它不马上产生代码.仅仅有在用到模板时,假设调用了函数模板或定义了模板的对象的时候,编译器才产生特定类型的模板实例. 一般而言,当调 ...

  6. C++ Primer学习笔记(三) C++中函数是一种类型!!!

    C++中函数是一种类型!C++中函数是一种类型!C++中函数是一种类型! 函数名就是变量!函数名就是变量!函数名就是变量! (---20160618最新消息,函数名不是变量名...囧) (---201 ...

  7. C++ Primer学习笔记(二)

    题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接  C++ Primer学习笔记(一)   27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...

  8. C++ Primer 学习笔记_77_模板与泛型编程 --实例化

    模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实 ...

  9. 【c++ Prime 学习笔记】第16章 模板与泛型编程

    面向对象编程(OOP)和泛型编程(GP)都能处理在编写程序时类型未知的情况 OOP能处理运行时获取类型的情况 GP能处理编译期可获取类型的情况 标准库的容器.迭代器.算法都是泛型编程 编写泛型程序时独 ...

随机推荐

  1. (总结)Nginx/LVS/HAProxy负载均衡软件的优缺点详解

    PS:Nginx/LVS/HAProxy是目前使用最广泛的三种负载均衡软件,本人都在多个项目中实施过,参考了一些资料,结合自己的一些使用经验,总结一下. 一般对负载均衡的使用是随着网站规模的提升根据不 ...

  2. Mybatis使用存储过程(MySql)

    推荐文章:http://www.iteye.com/topic/1132302 http://yhjhappy234.blog.163.com/blog/static/3163283220124557 ...

  3. springmvc入门demo

    目录结构: package com.wyl; import org.springframework.stereotype.Controller; import org.springframework. ...

  4. ASPから広がり

    ASP是动态服务器页面(Active Server Page)外语缩写.[1]是微软公司开发的代替CGI脚本程序的一种应用,它可以与数据库和其它程序进行交互,是一种简单.方便的编程工具.ASP的网页文 ...

  5. Asp MVC 中处理JSON 日期类型

    注:时间有点忙,直接copy 过来的,要查看原址: http://www.developer.com/net/dealing-with-json-dates-in-asp.net-mvc.html I ...

  6. Linux中的盘符问题

    在windows 中像 C.D.E.F这些都可以当盘符,就是说对应了我们所看到的C盘,D盘,E盘,F盘.然而是不是只能加26个硬盘了呢? 盘符到硬盘也只是一个对映关系,我们也是可以建立从一个文件夹到一 ...

  7. 原理图产生网络表后导进PADS之后,网络乱了的问题

    问题描述:在Orcad中生成的网络表(格式.ASC),导进PADS9.2中(PADS9.2中已有一些元器件),结果报Mixing nets,如下图示. 仔细检查原理图中的这些nets,发现有的有错,有 ...

  8. perl 爬取上市公司业绩预告

    <pre name="code" class="python">use LWP::UserAgent; use utf8; use DBI; use ...

  9. 动态链接库 DLL

    动态链接库DLL 不使用时不会有任何作用,只有在其他模块调用动态链接库中的函数时,它才发挥作用. 一.静态库与动态库 1.静态库 函数和数据被编译进一个二进制文件(.LIB),编译时,会将其组合起来创 ...

  10. Debug目录下没有.exe文件

    记一下小笔记: VC6.0设置.exe文件的输出路径: Project->Settings->Link Category选择"General" 在Output file ...