您的位置:首页 > 编程语言 > C语言/C++

C++快速入门 (十五) 模板

2013-02-25 14:09 561 查看

一,模板和泛型

(1). 泛型编程

一般的函数只能对固定某一系列(基类及其派生类)类型进行操作。如

[align=left]int Add(const int & t1, const int & t2)[/align]
[align=left]{[/align]
[align=left] return t1 + t2 ;[/align]
[align=left]}[/align]

该函数只能应用于 int 类型。显然不太利于重用(顺便提一句,面向对象的终极目标就是重用最大化)。如果写代码时可以使用某个占位符来替换实际的类型,编译时再确定,其重用性就大大提高。这种思想就叫做 泛型编程。C++中实现泛型的机制被称为模板。

(2). 模板函数

C++使用模板来实现泛型编程。模板使用 关键字template 声明,放在要定义泛型的函数或类之前。

[align=left]template <typename T>[/align]
[align=left]T Add(const T & t1 , const T & t2 )[/align]
[align=left]{[/align]
[align=left] return t1 + t2 ;[/align]
[align=left]}[/align]

其中 使用 关键字typename 来定义占位符名称并放在一对尖括号中。而泛型函数使用起来和普通函数一样。下边为一个完整示例

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] int num;[/align]
[align=left] Base operator+ ( const Base & ba) const[/align]
[align=left] {[/align]
[align=left] Base tem;[/align]
[align=left] tem.num = num + ba .num;[/align]
[align=left] return tem;[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]template <typename T>[/align]
[align=left]T Add(const T & t1 , const T & t2 )[/align]
[align=left]{[/align]
[align=left] return t1 + t2 ;[/align]
[align=left]}[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Base b1,b2;[/align]
[align=left] b1.num = 10;[/align]
[align=left] b2.num = 20;[/align]
[align=left] Base b3 = Add<Base>(b1,b2);[/align]
[align=left] cout << b3.num << endl;[/align]
[align=left]}[/align]
[align=left]//return: 30[/align]

模板函数在调用时也可以不声明或只声明一部分(未声明类型需放在最右边)模板参数类型,因为编译器会根据实参类型自动推导获得。

Base b3 = Add(b1,b2);

(3). 模板函数重载

模板函数也支持重载。无论是模板类型参数不同 或者 函数签名不同(一般重载)的同名函数,都视为重载。

[align=left]template <typename T>[/align]
T Add(const T & t1 , const T & t2 )
{....}
[align=left]template <typename T, typename C >[/align]
[align=left]T Add(const T & t , const C & c ) {....}[/align]

[align=left]template <typename T>[/align]
[align=left]T Add(const T & t) {....}[/align]

[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Add(2,2);[/align]
[align=left] Add(2,2.6);[/align]
[align=left] Add(10);[/align]
[align=left]}[/align]

(4). 模板类

同样也可以将一个普通类 通过模板 将其变为 泛型类。泛型类的声明方法和泛型函数相同。但泛型类实例化时需显式指定模板参数类型

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] int num;[/align]
[align=left] Base operator+ ( const Base & ba) const[/align]
[align=left] {[/align]
[align=left] Base tem;[/align]
[align=left] tem.num = num + ba .num;[/align]
[align=left] return tem;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]template <typename T>[/align]
[align=left]class Example[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] T obj;[/align]
[align=left] T Add(const T & t1 , const T & t2 ) const[/align]
[align=left] {[/align]
[align=left] return t1 + t2;[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Base b1,b2;[/align]
[align=left] b1.num = 10;[/align]
[align=left] b2.num = 20;[/align]
[align=left] Example< Base > ex;[/align]
[align=left] ex.obj = ex.Add(b1,b2);[/align]
[align=left] cout << ex.obj.num << endl;[/align]
[align=left]}[/align]
[align=left]//return: 30[/align]

(5). 模板类重载

模板类本身不支持重载(既不能通过模板类参数类型的不同进行重载定义),但模板类支持一种叫做模板特化的机制(后边会介绍)。 模板类的成员函数可以重载(和一般函数的重载条件相同)。

[align=left]template <typename T> [/align]
[align=left]class Dx[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] void Say( T t ) {....}[/align]
[align=left] void Say( T t , int x) {....}[/align]
[align=left]};[/align]

(6). 创建泛型类的实例

C++创建模板类的实例时,需要指定模板参数类型。如

[align=left]template <typename T>[/align]
[align=left]class Cx[/align]
......
Cx<int> cx;

Cx<char> cx2;

这是因为编译器编译时需将 模板参数类型转换为指定的实际类型,才能进行编译规则检查并创建该类型。一定要明白:同一个模板类不同模板参数类型所创建的是各自独立的类型。比如上边的两个Cx的实例为不同的类型。

(7). typename 和 class 的区别

早期的C++是使用 关键字class 声明模板类型, 后来由于 class 容易让人产生混淆,就加入了 关键字typename,但为了保证兼容性用 class 声明模板类型的方式 也保留了下来,既 声明模板类型时 typename 和 class 两个关键字可以相互替换

[align=left]template <class T> class Example [/align]

但 typename 有另外一个功能:当模板内使用模板类型的嵌套类型(嵌套类或 typeof 声明的别名等)时,为了避免歧义(比如声明方式和调用模板类型的静态函数一样)需在其前边添加关键字typename ,告诉编译器按类型处理。

[align=left]class Base[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] class Pocket // 嵌套类[/align]
[align=left] {[/align]
[align=left] ....[/align]
[align=left] };[/align]
[align=left]};[/align]
[align=left]template <typename T>[/align]
[align=left]class Example[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] T Add(const T & t1 , const T & t2 )[/align]
[align=left] {[/align]
[align=left] typename T ::Pocket poc; // typename 表明该声明为一个类型声明,而非调用静态成员[/align]
[align=left] ....[/align]
[align=left] return t1 + t2;[/align]
[align=left] }[/align]
[align=left]};[/align]

实际上对于现在的大多数编译器,不管你是否为类型添加 typename声明,都会正确的编译执行。

(8). 嵌套

C++可以将一个模板类型作为另一个模板类或函数的泛型类型。

[align=left]template <typename T>[/align]
[align=left]class Ax[/align]
[align=left]{[/align]
[align=left]};[/align]

[align=left]template <typename T>[/align]
[align=left]class Bx[/align]
[align=left]{[/align]
[align=left]};[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Ax<int> ax;[/align]
[align=left] Bx<Ax<int>> bx;[/align]
[align=left]}[/align]

也可以在一个模板类中包含另一个模板类或模板函数(成员模板)

[align=left]template <typename T>[/align]
[align=left]class Cx[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] template < typename Q >[/align]
[align=left] Q Say( const Q& q1 )[/align]
[align=left] {[/align]
[align=left] return q1 + 10;[/align]
[align=left] }[/align]
[align=left]};[/align]

[align=left]int _tmain ( int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] Cx< int> cx;[/align]
[align=left] short x = 200;[/align]
[align=left] cout << cx.Say(x) << endl;[/align]
[align=left]}[/align]

(9). 非类型模板形参

C++(c98)中允许使用整型常量(包括整型字面值常量和整型常量表达式)或枚举(整型)作为模板类的模板参数类型 。如下示例

[align=left]template <int num>[/align]
[align=left]class Cx[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] void Say()[/align]
[align=left] {[/align]
[align=left] cout << num << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
const int num = 30; //
需满足整型和常量两个条件。
[align=left] Cx<num> cx;[/align]
[align=left] cx.Say();[/align]
[align=left] Cx<20> cx2; // 整型常量。[/align]
[align=left] cx2.Say();[/align]
[align=left]}[/align]

二,模板特化

(1). 模板特化

当 模板类或模板函数 需要为个别模板参数类型进行特殊照顾(代码不同)时,就需要用到模板特化。特化以 关键字template 及其后跟一个空的尖括号开始。

(2). 模板函数特化

可以为模板函数定义模板特化。

[align=left]template <typename T>[/align]
[align=left]void Fun (const T& t )[/align]
[align=left]{[/align]
[align=left] cout << "非特化: " << t << endl;[/align]
[align=left]}[/align]
[align=left]template <>[/align]
[align=left]void Fun<int> ( const int& t )[/align]
[align=left]{[/align]
[align=left] cout << "特化: " << t << endl;[/align]
[align=left]}[/align]
[align=left]int _tmain ( int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] char x = 'a' ;[/align]
[align=left] int y = 20;[/align]
[align=left] Fun(x);[/align]
[align=left] Fun(y);[/align]
[align=left]}[/align]

(3). 模板类特化

也可以为 模板类成员定义模板特化,但比函数特化多一步,就是需要在特化类名后声明需特化的参数类型并放入尖括号括

[align=left]template <typename T>[/align]
[align=left]class Ax[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] T obj;[/align]
[align=left] void Fun ( const T & t )[/align]
[align=left] {[/align]
[align=left] cout << "非特化: " << t << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]template <>[/align]
[align=left]class Ax <int> // 特化类型[/align]
[align=left]{[/align]
[align=left]public :[/align]
[align=left] void Fun ( const int & t )[/align]
[align=left] {[/align]
[align=left] cout << "特化: " << t << endl;[/align]
[align=left] }[/align]
[align=left]};[/align]
[align=left]int _tmain (int argc, _TCHAR* argv [])[/align]
[align=left]{[/align]
[align=left] char x = 'a' ;[/align]
[align=left] int y = 20;[/align]
[align=left] Ax<char> a;[/align]
[align=left] a.Fun(x); // 非特化[/align]
[align=left] Ax<int> a2;[/align]
[align=left] a2.Fun(y); // 特化[/align]
[align=left]}[/align]

在类的特化版本需要列出该类中所有需特化的成员,既 只有类的特化版本中列出的成员才支持特化类型。比如上例中,成员变量 obj 在特化版本实例中将无法访问。

(4). 偏特化

偏特化是相对于全特化来说的。像上边这种将所有模板参数类型都转换为模板特化类型的模板特化方式叫做全特化。而偏特化既只转换原模板的部分参数类型。如

[align=left]template <typename T1, typename T2 >[/align]
[align=left]class Bx[/align]
[align=left]{[/align]
[align=left] ....[/align]
[align=left]};[/align]
[align=left]template <typename T1>[/align]
[align=left]class Bx <T1, int>[/align]
[align=left]{[/align]
[align=left] ....[/align]
[align=left]};[/align]

-

<原创文章 转载请注明出处 http://blog.csdn.net/meiwm 谢谢>

作者:meiwm

出处:http://blog.csdn.net/meiwm
本文为原创,本文版权归作者所有。欢迎转载,但请务必保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。

-
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: