c++的模板主要是用来解决代码重用的问题的。c++的模板有函数模板和类模
板两种。
函数模板
给你一个任务:写一个函数,给这个函数传递一个指向对象的指针,该函数
返回对象本身,要求这个对象的类型是任意的,你怎么写?
烦死人了是不是?难道我们必须针对所有的类型分别重载一个函数吗?没有
必要。用了函数模板之后,一切都简单了,我们只须象下面这样写:
template
MYTYPE fun(MYTYPE* a)
{
MYTYPE b;
b=*a;
return b;
}
上面这几条语句定义了一个函数fun,它有一个MYTYPE*类型的参数,它的返
回值是MYTYPE。奇妙之处就在这里,MYTYPE究竟代表什么类型是未定的,编译器
将在你具体引用这个函数fun时才从你传递给fun的参数类型来将MYTYPE替换成相
应的类型。感觉有点象宏指令里的参数代换。
我这个例子函数写得有点累赘,主要是想说明这个等待替换的MYTYPE可以出
现在函数返回类型里,函数的形式参数里和函数体内。
一旦定义了这个函数模板或者叫模板函数fun,就可以这样使用它了:
1:
int a=3,b;
b=fun(&a);
2:
CString s1="abcd",s2;
s2=fun(&s1)
3:
class MyClass
{
.....
};
MyClass* a,b,c;
a=&b;
c=fun(a);//要求你的MyClass类型必须重载了赋值运算符。
可见引用模板函数和引用一般的函数在语法形式上没有任何区别。所有的类
型替换都是编译器在幕后悄悄进行的。
如果是在定义模板函数之前就要引用模板函数的话,和普通函数类似,也得
先进行函数原形声明。模板函数原形的格式和普通函数原形稍微不同。以上面的
例子来说明,fun的原形可以这样写:
template
MYTYPE fun(MYTYPE* a);
实际上就比普通函数多了一个template <...>。
总结一下如何定义函数模板。首先是关键字template,后跟用尖括号括起来
的模板参数列表。然后是和一般函数定义一样的函数定义。
模板参数列表里的参数是类型代用符号,前面用关键字class指明,它可以用
在函数返回类型,函数形参类型和函数体内。
上面的例子说明了,如果想对所有的参数类型类型都做同样的操作,那么可
以用函数模板。如果我们偏偏就想对int类型做点特殊的操作,又该如何使用函数
模板?用上面的例子来说明,如果我们对于int类型的参数,想返回该整型值乘2
,其他类型的参数就返回参数本身,那么函数模板可以这样写:
template
MYTYPE fun(MYTYPE* a)
{
MYTYPE b;
b=*a;
::MessageBox(NULL,"其他类型参数","",MB_OK);
return b;
}
//上面先定义一个函数模板,下面把需要单独处理的参数类型做特别定义。
template<> fun(int* a)
{
::MessageBox(NULL,"整形参数","",MB_OK);
return 2*(*a);
}
可以这样来引用fun函数:
int a=5,b;
char *ch="abcd";
b=fun(&a);
fun(ch);
编译一下,可以看到,第一次对fun的引用,显示"整型参数",第二次引用
显示"其他类型参数"。
类模板
如果你的程序里用了几个类,这几个类大同小异,那你就可以考虑是否可以
使用类模板来简化你的程序。例子:
template
class myclass
{
private:
T array[var];
public:
T GetItem(int);
T SetItem(T,int);
};
上面是模板类的声明,下面是类中两个成员函数的声明:
template
T myclass::GetItem(int n)
{
if(n>=0&&n}
template
T myclass::SetItem(T t,int n)
{
if(n>=0&&n}
从上面可以看出,声明一个类模板时,一开始是关键字template,然后是尖
括号括起来的模板参数表。模板参数表里的参数分两类,第一类是类型代用符号
,前面用关键字class指明,第二类是变量。类型代用符和变量可以有多个。接下
来就是一般的类声明了,你可以把模板参数列表里的类型代用符和变量用在类声
明里。
模板类的成员函数的实现语句和普通类的成员函数的实现语句是差不多的,
只是一开始多了模板关键字template及模板参数表,然后在模板类的类名和两个
冒号之间多了些用尖括号括起来的东西。
类模板已经声明完了,该如何声明它的对象呢?我们必须在声明对象时显式
指明模板参数列表里各个参数的值。让我们用上面声明的类模板声明几个对象:
myclass ob1,*ob2;
myclass ob3,*ob4;
myclass ob5,*ob6;
从概念上说,myclass只是一个模板,其中的参数待定。仅当所有的模板参数
都已经指明了后,myclass,myclass和myclass>才是实实在在的可以声明对象的类,而且虽然他们都有一个共同的模板名字"m
yclass",但由于模板参数不同,他们却实实在在不是同一个类。所以ob1,ob3,
ob5并不是同一个类的对象,ob2,ob4,ob6也不是同一类型的指针。
具体说来,myclass是这样一个类:它有一个包含10个int类型单元
的数组成员,有两个分别读取和设置整型数组单元值的成员函数。
myclass是这样一个类:它有一个包含20个float类型单元的数组
成员,有两个分别读取和设置浮点数组单元值的成员函数。
myclass是这样一个类:它有一个包含15个CString类型单元的
数组成员,有两个分别读取和设置CString类型数组单元值的成员函数。
如果我们用模板类myclass来这样声明一个对象ob:
class dataclass
{
.....
};
myclass ob;
如果你在dataclass类里重载了赋值号操作符,那么上述声明是允许的,否则
是不允许的。因为在成员函数SetItem里将对dataclass类型使用赋值操作符。
同模板函数一样,我们也可以指明对某种模板参数类型,我们的类将有不同
于别的类的声明。例如:
template
class myclass
{
public:
void Show();
};
template
void myclass::Show()
{
::MessageBox(NULL,"其他类型","",MB_OK);
}
//上面声明了一个适合所有类型的模板类,下面对char类型做特别声明。
template<> class myclass
{
public:
void Show();
};
template<>
void myclass::Show()
{
::MessageBox(NULL,"char 类型","",MB_OK);
}
然后我们用模板类myclass来声明两个对象:
myclass var1;
myclass var2;
myclass var3;
var1.Show();
var2.Show();
var3.Show();
结果是先显示两次"其他类型",然后显示"char 类型"。说明编译器确实
把char类型单列出来处理了。
如果要设计一个数组类,它维护一个数组,可以对数组单元进行增加,删除
,查找等操作,而且要求该数组的单元可以是任何已知和未知类型,该怎么设计
?当然是用模板类。MFC中的CArray类正是这样设计的。感兴趣的人可以看看文件
Afxtempl.h,里面是CArray类的声明和成员函数的实现。
本文中的例子在vc++6.0里编译通过。
板两种。
函数模板
给你一个任务:写一个函数,给这个函数传递一个指向对象的指针,该函数
返回对象本身,要求这个对象的类型是任意的,你怎么写?
烦死人了是不是?难道我们必须针对所有的类型分别重载一个函数吗?没有
必要。用了函数模板之后,一切都简单了,我们只须象下面这样写:
template
MYTYPE fun(MYTYPE* a)
{
MYTYPE b;
b=*a;
return b;
}
上面这几条语句定义了一个函数fun,它有一个MYTYPE*类型的参数,它的返
回值是MYTYPE。奇妙之处就在这里,MYTYPE究竟代表什么类型是未定的,编译器
将在你具体引用这个函数fun时才从你传递给fun的参数类型来将MYTYPE替换成相
应的类型。感觉有点象宏指令里的参数代换。
我这个例子函数写得有点累赘,主要是想说明这个等待替换的MYTYPE可以出
现在函数返回类型里,函数的形式参数里和函数体内。
一旦定义了这个函数模板或者叫模板函数fun,就可以这样使用它了:
1:
int a=3,b;
b=fun(&a);
2:
CString s1="abcd",s2;
s2=fun(&s1)
3:
class MyClass
{
.....
};
MyClass* a,b,c;
a=&b;
c=fun(a);//要求你的MyClass类型必须重载了赋值运算符。
可见引用模板函数和引用一般的函数在语法形式上没有任何区别。所有的类
型替换都是编译器在幕后悄悄进行的。
如果是在定义模板函数之前就要引用模板函数的话,和普通函数类似,也得
先进行函数原形声明。模板函数原形的格式和普通函数原形稍微不同。以上面的
例子来说明,fun的原形可以这样写:
template
MYTYPE fun(MYTYPE* a);
实际上就比普通函数多了一个template <...>。
总结一下如何定义函数模板。首先是关键字template,后跟用尖括号括起来
的模板参数列表。然后是和一般函数定义一样的函数定义。
模板参数列表里的参数是类型代用符号,前面用关键字class指明,它可以用
在函数返回类型,函数形参类型和函数体内。
上面的例子说明了,如果想对所有的参数类型类型都做同样的操作,那么可
以用函数模板。如果我们偏偏就想对int类型做点特殊的操作,又该如何使用函数
模板?用上面的例子来说明,如果我们对于int类型的参数,想返回该整型值乘2
,其他类型的参数就返回参数本身,那么函数模板可以这样写:
template
MYTYPE fun(MYTYPE* a)
{
MYTYPE b;
b=*a;
::MessageBox(NULL,"其他类型参数","",MB_OK);
return b;
}
//上面先定义一个函数模板,下面把需要单独处理的参数类型做特别定义。
template<> fun(int* a)
{
::MessageBox(NULL,"整形参数","",MB_OK);
return 2*(*a);
}
可以这样来引用fun函数:
int a=5,b;
char *ch="abcd";
b=fun(&a);
fun(ch);
编译一下,可以看到,第一次对fun的引用,显示"整型参数",第二次引用
显示"其他类型参数"。
类模板
如果你的程序里用了几个类,这几个类大同小异,那你就可以考虑是否可以
使用类模板来简化你的程序。例子:
template
class myclass
{
private:
T array[var];
public:
T GetItem(int);
T SetItem(T,int);
};
上面是模板类的声明,下面是类中两个成员函数的声明:
template
T myclass::GetItem(int n)
{
if(n>=0&&n}
template
T myclass::SetItem(T t,int n)
{
if(n>=0&&n}
从上面可以看出,声明一个类模板时,一开始是关键字template,然后是尖
括号括起来的模板参数表。模板参数表里的参数分两类,第一类是类型代用符号
,前面用关键字class指明,第二类是变量。类型代用符和变量可以有多个。接下
来就是一般的类声明了,你可以把模板参数列表里的类型代用符和变量用在类声
明里。
模板类的成员函数的实现语句和普通类的成员函数的实现语句是差不多的,
只是一开始多了模板关键字template及模板参数表,然后在模板类的类名和两个
冒号之间多了些用尖括号括起来的东西。
类模板已经声明完了,该如何声明它的对象呢?我们必须在声明对象时显式
指明模板参数列表里各个参数的值。让我们用上面声明的类模板声明几个对象:
myclass ob1,*ob2;
myclass ob3,*ob4;
myclass ob5,*ob6;
从概念上说,myclass只是一个模板,其中的参数待定。仅当所有的模板参数
都已经指明了后,myclass,myclass和myclass>才是实实在在的可以声明对象的类,而且虽然他们都有一个共同的模板名字"m
yclass",但由于模板参数不同,他们却实实在在不是同一个类。所以ob1,ob3,
ob5并不是同一个类的对象,ob2,ob4,ob6也不是同一类型的指针。
具体说来,myclass是这样一个类:它有一个包含10个int类型单元
的数组成员,有两个分别读取和设置整型数组单元值的成员函数。
myclass是这样一个类:它有一个包含20个float类型单元的数组
成员,有两个分别读取和设置浮点数组单元值的成员函数。
myclass是这样一个类:它有一个包含15个CString类型单元的
数组成员,有两个分别读取和设置CString类型数组单元值的成员函数。
如果我们用模板类myclass来这样声明一个对象ob:
class dataclass
{
.....
};
myclass ob;
如果你在dataclass类里重载了赋值号操作符,那么上述声明是允许的,否则
是不允许的。因为在成员函数SetItem里将对dataclass类型使用赋值操作符。
同模板函数一样,我们也可以指明对某种模板参数类型,我们的类将有不同
于别的类的声明。例如:
template
class myclass
{
public:
void Show();
};
template
void myclass::Show()
{
::MessageBox(NULL,"其他类型","",MB_OK);
}
//上面声明了一个适合所有类型的模板类,下面对char类型做特别声明。
template<> class myclass
{
public:
void Show();
};
template<>
void myclass::Show()
{
::MessageBox(NULL,"char 类型","",MB_OK);
}
然后我们用模板类myclass来声明两个对象:
myclass var1;
myclass var2;
myclass var3;
var1.Show();
var2.Show();
var3.Show();
结果是先显示两次"其他类型",然后显示"char 类型"。说明编译器确实
把char类型单列出来处理了。
如果要设计一个数组类,它维护一个数组,可以对数组单元进行增加,删除
,查找等操作,而且要求该数组的单元可以是任何已知和未知类型,该怎么设计
?当然是用模板类。MFC中的CArray类正是这样设计的。感兴趣的人可以看看文件
Afxtempl.h,里面是CArray类的声明和成员函数的实现。
本文中的例子在vc++6.0里编译通过。