程序设计中,会遇到这样的问题:程序实体所要完成的功能完全一样,但是它们所操作的数据类型却不一样。对于这些函数或者类,采用一个函数和一个类来进行描述的话,将会大大简化程序设计的工作。
比如求最大值max()的实现:有int、int ; double、double ; float、float……需要定义多个函数。但是这些函数的实现,除了数据类型不同,其他都相同。使用函数模板可以简化程序。
template //也可以使用template
T max(T a, T b)
{
if (a > b)
return a;
else
return b;
}
有了函数模板,那么在使用类的时候,成员函数、友元函数如何使用函数模板呢?这就需要类模板。
//表示矩阵,可以自定义行列数,值可以是不同数据类型
template
class matrix {
C(*p)[N];
public:
matrix();
~matrix();
};
声明一系列泛型,仅作用与之后的一个函数,在不同函数中,与名称T无关,代表不同的含义。
除了声明类型,还可以声明数值
template
基本上可以与class互换。因为使用class易混淆,故引入typename。
与class不同的是,typename可以告诉编译器其后是类型名。常在嵌套依赖类型(nested depended name)中使用,简化表达。
//嵌套依赖类型
template
class text1
{
public:
typedef int MYINT; //必须共有,否则无法访问
private:
MYINT a;
};
template
class test2
{
typedef typename TP2::MYINT MY_INT;
MY_INT b;
};
//调用语句
test2> n;
定义类模板可以在成员友元函数中使用泛型,简化程序。同函数模板使用基本相同。不同的是,在类的使用时,定义对象(实例化)的时候需要显示写出泛型。如上例
//调用语句
test2> n;
体内实现时和普通类一样
体外实现时声明同普通类一样,定义时要加上泛型声明。
体内实现时要加上泛型声明(一处)。
体外实现时声明和定义时都要加上泛型声明(两处)。
但是体外实现可移植性较差,vs可使用,gcc系列编译器和linux都不支持这种写法。故友元函数最好写成体内实现形式。
而友元函数不能体外实现的原因也很简单。上文提到template泛型声明仅作用于一个函数,与typename泛型名相同与否无关。故友元函数体外实现时,编译器看来就是另一个使用模板的函数,与类声明的泛型无关。在调用函数的时候,该函数无法知道如同定义时显示写出的泛型。而友元声明本身只是允许访问私有成员。故无法体外实现。vs对其进行了特殊处理。
举例:
template
class matrix {
C(*p)[N];
public:
//成员函数 构造析构
//体内实现
matrix();
{
p = new C[M][N];
}
//体外实现 声明
~matrix();
//友元函数 体内实现
friend istream& operator>>(istream& in, matrix& s)
{
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++)
in >> s.p[i][j];
return in;
}
};
//成员 体外 定义
template
matrix::~matrix()
{
delete[]p;
}
//调用
int main()
{
const int m = 2, n = 3; //必须const
matrix matri; //调用构造
cin >> matri; //调用重载运算符
//调用析构
return 0;
}