一,模板和泛型
(1). 泛型编程
一般的函数只能对固定某一系列(基类及其派生类)类型进行操作。如
int
Add(
const
int
&
t1,
const
int
&
t2)
{
return
t1
+
t2
;
}
该函数只能应用于 int 类型。显然不太利于重用(顺便提一句,面向对象的终极目标就是重用最大化)。如果写代码时可以使用某个占位符来替换实际的类型,编译时再确定,其重用性就大大提高。这种思想就叫做 泛型编程。C++中实现泛型的机制被称为模板。
(2). 模板函数
C++使用模板来实现泛型编程。
模板使用 关键字template 声明,放在要定义泛型的函数或类之前。
template <typename T>
T
Add(
const
T
&
t1
,
const
T
&
t2
)
{
return
t1
+
t2
;
}
其中
使用 关键字
typename 来定义占位符名称并放在一对尖括号中。而泛型函数使用起来和普通函数一样。下边为一个完整示例
class
Base
{
public
:
int
num;
Base
operator+ (
const
Base
&
ba)
const
{
Base
tem;
tem.num = num +
ba
.num;
return
tem;
}
};
template <typename T>
T Add(const T & t1 , const T & t2 )
{
return
t1
+
t2
;
}
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Base
b1,b2;
b1.num = 10;
b2.num = 20;
Base
b3 =
Add<Base>(b1,b2)
;
cout << b3.num << endl;
}
//return: 30
模板
函数在调用时也可以不声明或只声明一部分(未声明类型需放在最右边)模板参数类型,因为编译器会根据实参类型自动推导获得。
Base
b3 =
Add(b1,b2)
;
(3). 模板函数重载
模板函数也支持重载。无论是模板类型参数不同 或者 函数签名不同(一般重载)
的同名函数
,都视为重载。
template
<typename T>
T
Add(
const
T
&
t1
,
const
T
&
t2
) {....}
template
<typename T, typename C >
T
Add(
const
T
&
t
,
const
C
&
c
) {....}
template
<
typename
T>
T
Add(
const T & t) {....}
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Add(2,2);
Add(2,2.6);
Add(10);
}
(4). 模板类
同样也可以将一个普通类 通过模板 将其变为 泛型类。泛型类的声明方法和泛型函数相同。但
泛型类实例化时需显式指定模板参数类型。
class
Base
{
public
:
int
num;
Base
operator+ (
const
Base
&
ba)
const
{
Base
tem;
tem.num = num +
ba
.num;
return
tem;
}
};
template <typename T>
class
Example
{
public
:
T obj;
T Add(const T & t1 , const T & t2 ) const
{
return
t1
+
t2;
}
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Base
b1,b2;
b1.num = 10;
b2.num = 20;
Example< Base > ex;
ex.obj = ex.Add(b1,b2);
cout << ex.obj.num << endl;
}
//return: 30
(5). 模板类重载
模板类本身不支持重载(既不能通过模板类参数类型的不同进行重载定义),但模板类支持一种叫做模板特化的机制(后边会介绍)。 模板类的成员函数可以重载(和一般函数的重载条件相同)。
template
<
typename
T>
class
Dx
{
public
:
void Say( T t ) {....}
void Say( T t , int x) {....}
};
(6). 创建泛型类的实例
C++创建模板类的实例时,需要指定模板参数类型。如
template <typename T>
class
Cx
......
Cx
<int
> cx;
Cx
<char> cx2;
这是因为编译器编译时需将 模板参数类型转换为指定的实际类型,才能进行编译规则检查并创建该类型。一定要明白:
同一个模板类不同模板参数类型所创建的是各自独立的类型。比如上边的两个Cx的实例为不同的类型。
(7). typename 和 class 的区别
早期的C++是使用 关键字class 声明模板类型, 后来由于 class 容易让人产生混淆,就加入了 关键字typename,但为了保证兼容性用 class 声明模板类型的方式 也保留了下来,既
声明模板类型时 typename 和 class 两个关键字可以相互替换。
template <class T>
class
Example
但 typename 有另外一个功能:当模板内
使用模板类型的嵌套类型(嵌套类或 typeof 声明的别名等)
时,为了避免歧义(比如声明方式和调用模板类型的静态函数一样)需
在其前边添加关键字typename ,告诉编译器按类型处理。
class
Base
{
public
:
class
Pocket
// 嵌套类
{
....
};
};
template
<
typename
T>
class
Example
{
public
:
T
Add(
const
T
&
t1
,
const
T
&
t2
)
{
typename
T
::
Pocket
poc;
//
typename
表明该声明为一个类型声明,而非调用静态成员
....
return
t1
+
t2;
}
};
实际上对于现在的大多数编译器,不管你是否为类型添加 typename声明,都会正确的编译执行。
(8). 嵌套
C++可以将一个模板类型作为另一个模板类或函数的泛型类型。
template
<
typename
T>
class
Ax
{
};
template
<
typename
T>
class
Bx
{
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Ax<
int> ax;
Bx<
Ax
<int>> bx;
}
也可以在一个模板类中包含另一个模板类或模板函数(成员模板)
template <typename T>
class
Cx
{
public :
template < typename Q >
Q Say(
const
Q&
q1 )
{
return
q1 + 10;
}
};
int
_tmain (
int
argc,
_TCHAR*
argv [])
{
Cx<
int> cx;
short x = 200;
cout << cx.Say(x) << endl;
}
(9). 非类型模板形参
C++(c98)中允许使用整型常量(包括整型字面值常量和整型常量表达式)或枚举(整型)作为模板类的模板参数类型 。如下示例
template
<
int num>
class
Cx
{
public
:
void
Say()
{
cout <<
num << endl;
}
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
const int
num = 30;
// 需满足整型和常量两个条件。
Cx<
num> cx;
cx.Say();
Cx<
20> cx2;
// 整型常量。
cx2.Say();
}
二,模板特化
(1). 模板特化
当 模板类或模板函数 需要为个别模板参数类型进行特殊照顾(代码不同)时,就需要用到模板特化。
特化以 关键字template 及其后跟一个空的尖括号开始。
(2). 模板函数特化
可以为模板函数定义模板特化。
template <typename T>
void Fun
(const T& t )
{
cout <<
"非特化: " <<
t << endl;
}
template <>
void Fun
<int> ( const int& t )
{
cout <<
"特化: " <<
t << endl;
}
int
_tmain (
int
argc,
_TCHAR*
argv [])
{
char x =
'a' ;
int y = 20;
Fun(x);
Fun(y);
}
(3). 模板类特化
也可以为 模板类成员定义模板特化,但比函数特化多一步,就是
需要在特化类名后声明需特化的参数类型并放入尖括号括。
template <typename T>
class
Ax
{
public
:
T obj;
void
Fun (
const T & t )
{
cout <<
"非特化: "
<<
t
<< endl;
}
};
template <>
class
Ax
<int>
// 特化类型
{
public
:
void
Fun
( const int & t )
{
cout <<
"特化: "
<<
t
<< endl;
}
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
char
x =
'a'
;
int
y = 20;
Ax<char> a;
a.Fun(x);
// 非特化
Ax<int> a2;
a2.Fun(y);
// 特化
}
在类的特化版本需要列出该类中所有需特化的成员,既
只有类的特化版本中列出的成员才支持特化类型。比如上例中,成员变量 obj 在特化版本实例中将无法访问。
(4). 偏特化
偏特化是相对于全特化来说的。像上边这种将所有模板参数类型都转换为模板特化类型的模板特化方式叫做全特化。而偏特化既只转换原模板的部分参数类型。如
template
<
typename
T1,
typename
T2
>
class
Bx
{
....
};
template
<
typename
T1>
class
Bx
<
T1,
int>
{
....
};
-
<原创文章 转载请注明出处 http://blog.csdn.net/meiwm 谢谢>
出处:
http://blog.csdn.net/meiwm
本文为原创,本文版权归作者所有。欢迎转载,但请务必保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。
-
本文详细介绍了C++中的模板和泛型编程,涵盖模板函数、模板类、重载、typename与class的区别、嵌套、非类型模板形参等内容,并进一步探讨了模板特化,包括函数和类模板的特化以及偏特化,为C++开发者提供了全面的模板使用指南。
8248

被折叠的 条评论
为什么被折叠?



