C++学习笔记(十四):模板

模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。
模版可以分为两类,一个是函数模版,另外一个是类模版。
Java中对应的技术称为泛型。

函数模板:

格式:

1 template <class 形参名,class 形参名,......>
2 返回类型 函数名(参数列表)
3 {
4     函数体
5 }

其中template和class是关见字,class可以用typename 关见字代替,在这里typename 和class没区别,<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。
一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。

例如:

 1 #include <iostream>
 2
 3 using std::cout;
 4 using std::endl;
 5
 6 //声明一个函数模版,用来比较输入的两个相同数据类型的参数的大小,class也可以被typename代替,
 7 //T可以被任何字母或者数字代替。
 8 template <class T>
 9 T min(T x,T y)
10 {
11      return(x<y)?x:y;
12 }
13
14 void main( )
15 {
16      int n1=2,n2=10;
17      double d1=1.5,d2=5.6;
18      cout<< "较小整数:"<<min(n1,n2)<<endl;
19      cout<< "较小实数:"<<min(d1,d2)<<endl;
20      system("PAUSE");
21 }

类模板:

格式:

1 template<class 形参名,class 形参名,…>
2 class 类名
3 {
4     ...
5 };

类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。

在类模板外部定义成员函数的方法为:

1 template<模板形参列表> 函数返回类型 类名<模板形参名>::函数名(参数列表){函数体}

例如比如有两个模板形参T1,T2的类A中含有一个void h()函数,则定义该函数的语法为:

1 template<class T1,class T2> void A<T1,T2>::h(){}

示例:

 1 // ClassTemplate.h
 2 #ifndef ClassTemplate_HH
 3
 4 #define ClassTemplate_HH
 5
 6 template<typename T1,typename T2>
 7
 8 class myClass{
 9
10 private:
11
12      T1 I;
13
14      T2 J;
15
16 public:
17
18      myClass(T1 a, T2 b);//Constructor
19
20      void show();
21
22 };
23
24 //这是构造函数
25 //注意这些格式
26 template <typename T1,typename T2>
27 myClass<T1,T2>::myClass(T1 a,T2 b):I(a),J(b){}
28
29 //这是void show();
30 template <typename T1,typename T2>
31 void myClass<T1,T2>::show()
32 {
33
34      cout<<"I="<<I<<", J="<<J<<endl;
35
36 }
37
38 #endif
39
40 // Test.cpp
41
42 #include <iostream>
43
44 #include "ClassTemplate.h"
45
46 using std::cout;
47
48 using std::endl;
49
50 void main()
51
52 {
53
54      myClass<int,int> class1(3,5);
55
56      class1.show();
57
58      myClass<int,char> class2(3,‘a‘);
59
60      class2.show();
61
62      myClass<double,int> class3(2.9,10);
63
64      class3.show();
65
66      system("PAUSE");
67
68 }

模板的形参

(1)类型形参

类型形参由关见字class或typename后接说明符构成。
模板形参表示的是一个未知的类型。
模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。

如:

1 template<class T> void h(T a){};//其中T就是一个类型形参,类型形参的名字由用户自已确定

(2)非类型形参

  1. 模板的非类型形参也就是内置类型形参,如template<class T, int a> class B{};其中int a就是非类型的模板形参。
  2. 非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。
  3. 非类型模板的形参只能是整型,指针和引用,像double,String, String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。
  4. 调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
  5. 任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
  6. 全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
  7. sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
  8. 当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如template <class T, int a> class A{};如果有int b,这时A<int, b> m;将出错,因为b不是常量,如果const int b,这时A<int, b> m;就是正确的,因为这时b是常量。
  9. 非类型形参一般不应用于函数模板中,比如有函数模板template<class T, int a> void h(T b){},若使用h(2)调用会出现无法为非类型形参a推演出参数的错误,对这种模板函数可以用显示模板实参来解决,如用h<int, 3>(2)这样就把非类型形参a设置为整数3。显示模板实参在后面介绍。

非类型模板形参的形参和实参间所允许的转换:

  • 允许从数组到指针,从函数到指针的转换。如:template <int *a> class A{}; int b[1]; A<b> m;即数组到指针的转换
  • const修饰符的转换。如:template<const int *a> class A{}; int b; A<&b> m; 即从int *到const int *的转换。
  • 提升转换。如:template<int a> class A{}; const short b=2; A<b> m; 即从short到int 的提升转换
  • 整值转换。如:template<unsigned int a> class A{}; A<3> m; 即从int 到unsigned int的转换。
  • 常规转换。

(3)模板形参(以模板作为模板的参数)

就是将一个模板作为另一个模板的参数。
例子:

1 Grid<int,vector<int> > myIntGrid;  

注意其中int出现了两次,必须指定Grid和vector的元素类型都是int。
如果写成:

1 Grid<int,vector> myIntGrid;  

因为vector本身就是一个模板,而不是一个类型,所以这就是一个模板模板参数。指定模板模板参数有点像在常规的函数中指定函数指针参数。
函数指针类型包括返回类型和函数的参数类型。在声明模板模板参数的时候也要包括完整的模板声明:
首先要知道作为参数的模板的原型,比如vector

1 template<typename E,typename Allocator=allocator<E> >
2 class vector
3 {...};  

然后就可以定义:

1 template<typename T,template<typename E,typename Allocator=allocator<E> >class Container=vector>
2 class Grid
3 {
4 public:
5  //Omitted for brevity
6  Container<T>* mCells;
7 };  
时间: 2024-10-10 02:57:43

C++学习笔记(十四):模板的相关文章

Swift学习笔记十四:构造(Initialization)

类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值.存储型属性的值不能处于一个未知的状态. 你可以在构造器中为存储型属性赋初值,也可以在定义属性时为其设置默认值.以下章节将详细介绍这两种方法. 注意: 当你为存储型属性设置默认值或者在构造器中为其赋值时,它们的值是被直接设置的,不会触发任何属性观测器(property observers). 一.基本语法 class Human{ var name :String init(){ name = "human" } init(n

C++学习笔记十六-模板和泛型编程(二)

C++学习笔记十六-模板和泛型编程(二) 16.4 类模板成员 1.模板作用域中模板类型的引用: 通常,当使用类模板的名字的时候,必须指定模板形参.这一规则有个例外:在类本身的作用域内部,可以使用类模板的非限定名.例如,在默认构造函数和复制构造函数的声明中,名字 Queue 是 Queue<Type> 缩写表示.实质上,编译器推断,当我们引用类的名字时,引用的是同一版本.因此,复制构造函数定义其实等价于: Queue<Type>(const Queue<Type> &a

laravel3学习笔记(十四)

原作者博客:ieqi.net ==================================================================================================== 运行时配置 在 Laravel3 中很多地方我们都可以看到“约定大于配置”的影子,我本人也很喜欢这种工程哲学尤其是在框架领域,当然这并不能代替所有的配置.我们知道 Laravel3 中,主要配置都写在 application/config 文件夹下,在应用逻辑中,往往

yii2源码学习笔记(十四)

Module类是模块和应用类的基类. yiisoft\yii2\base\Module.php 1 <?php 2 /** 3 * @link http://www.yiiframework.com/ 4 * @copyright Copyright (c) 2008 Yii Software LLC 5 * @license http://www.yiiframework.com/license/ 6 */ 7 8 namespace yii\base; 9 10 use Yii; 11 us

【转】angular学习笔记(十四)-$watch(1)

本篇主要介绍$watch的基本概念: $watch是所有控制器的$scope中内置的方法: $scope.$watch(watchObj,watchCallback,ifDeep) watchObj: 需要被检测的对象,可以是以下任意一种: 1. 某个数据,监测这个数据的值是否发生变化 2. 一条angular表达式,监测表达式的结果是否发生变化 3. 函数(),监测函数的返回值是否发生变化 注意,以上三种,无论是哪种,都应该是字符串格式,并且都是在$scope作用域下执行的. 4.函数,非字符

Android学习笔记十四.深入理解fragment(二) 之《图书详情》实战

深入理解fragment(二) 之<图书详情>实战 通过上一篇博文<深入理解fragment一>,我们学习了Android-Fragment的核心知识点.现在在此基础上,利用Fragment技术开发一款适用于大屏幕手机/平板的查找图书详情的应用软件.该项目主要在于两方面,一是Activity.Fragment的源码实现:二是,布局界面资源文件的实现. 1.res/../BookListFragment.java: 自定义类,继承于ListFragment,无需实现OnCreateV

JavaScript权威设计--Window对象之Iframe(简要学习笔记十四)

1.Window对象属性的文档元素(id) 如果在HTML文档中用id属性来为元素命名,并且如果Window对象没有此名字的属性,Window对象会赋予一个属性,它的名字是id属性的值,而他们的值指向表示文档元素的HTMLElement对象. Window对象是以全局对象的形式存在于作用域链的最上层,这就意味着HTML文档中使用的id属性会成为可以被脚本访问的全局变量. 如: <button id="but"/> 就可以通过全局变量but来引用此元素. 2.多窗体窗口(if

Oracle学习笔记十四 内置程序包

扩展数据库的功能 为 PL/SQL 提供对 SQL 功能的访问 用户 SYS 拥有所有程序包 是公有同义词 可以由任何用户访问 一些内置程序包 程序包名称 说明 STANDARD和DBMS_STANDARD 定义和扩展PL/SQL语言环境 DBMS_LOB 提供对 LOB数据类型进行操作的功能 DBMS_OUTPUT 处理PL/SQL块和子程序输出调试信息 DBMS_RANDOM 提供随机数生成器 DBMS_SQL 允许用户使用动态 SQL DBMS_XMLDOM 用DOM模型读写XML类型的数

Java中执行存储过程和函数(web基础学习笔记十四)

一.概述 如果想要执行存储过程,我们应该使用 CallableStatement 接口. CallableStatement 接口继承自PreparedStatement 接口.所以CallableStatement 接口包含有Statement 接口和PreparedStatement 接口定义的全部方法,但是并不是所有的方法我们都要使用,主要使用的方法有这样几个: CallableStatement 常用方法: 返回类型 方法签名 说明 boolean execute() 执行 SQL 语句

PHP学习笔记十四【面向对象】

<?php class Cat{ public $name; public $age; public $color; } //创建一个对象 $cat1=new Cat(); $cat1->name="小白"; //给对象成员赋值 $cat1->age=3; $cat1->color="白色"; echo $cat1->name."||".$cat1->age."||".$cat1->c