基本技能3:通用的类
除了上面的通用函数以外,我们还可以创建通用的类。创建通用类的实质是定义了这个通用类使用到的算法。算法中实际使用到的数据的类型是在对该类进行实例化的时候通过参数的方式传入的。
当一个类提供的逻辑可以通用化时,使用通用类此时会非常显得非常有用。例如,用来处理元素类型为整形数的队列的算法同样适用于元素类型为字符的队列;用来处理元素为电子邮件地址的链表的算法也同样适用于元素为汽车配件信息的链表。一旦定义了一个通用的类,该类就能处理任何类型的数据。例如上面提到的队列和链表。在我们对该通用类进行实例化的时候,编译器会自动地根据我们传入的数据类型来生成正确的数据对象。
通用的声明通用类的方式如下:
template<class Ttype>class通用类名称
{
//类的实现
}
其中的Ttype就是类型占位符。当我们使用通用类来生成对象的时候就需要指定该类型。我们还可以为通用类定义多个类型占位符。只要把他们写在尖括号中,用逗号隔开即可。
一旦创建了一个通用的类,我们就可以使用如下的方式来生成特定的对象:
通用类名称<类型>对象名;
其中的类型就是这个类所要操作的数据的类型。类的成员函数由编译器自动生成,我们不必显示地指定这些成员函数。
下面就是一个简单的通用类的示例程序:
//一个简单的通用类
#include<iostream>
using namespace std;
//声明一个通用的类,其中的T是通用的数据类型
template<class T> class MyClass
{
T x,y;
public:
MyClass(T a, T b)
{
x = a;
y = b;
}
T div()
{
return x/y;
}
};
int main()
{
//创建double类型的MyClass的对象
MyClass<double> d_ob(10.0,3.0); //创建通用类的一个特定的实例
cout <<"double division: " << d_ob.div() <<"\n";
//创建int类型的MyClass的对象
MyClass<int> i_ob(10,3);
cout <<"integer division: " << i_ob.div() <<"\n";
return 0;
};
上面程序的输出如下:
double division: 3.33333
integer division: 3
正如程序输出的那样,针对double类型的对象进行的是浮点数的除法运算;而针对整型数的对象则进行的是整型数的除法。
当我们声明通用类MyClass的对象的时候,编译器自动地针对传入的数据类型生成了div()函数,x,y的类型也由编译器自动根据传入参数的类型来进行声明。在上面的这个示例程序中,我们声明了两个不同类型的对象。第一个,d_ob操作的是double类型的数据,这就意味着x,y是double类型的,并且除法运算的结果也是double类型的。第二个,i_ob操作的是int类型的,因此x,y都是int类型的,除法运算的结果也是int类型的。注意其中的声明方式:
MyClass<double> d_ob(10.0,3.0);
MyClass<int> i_ob(10,3);
所期望的数据类是被放置在通用类名称后面的尖括号中。通过在声明对象的时候传入不同的类型,我们就可以修改MyClass类的操作对象的类型。
通用的类也可以有多个通用的数据类型。只要在模板声明时把这多个类型放置在尖括号中,并用逗号隔开即可。例如,下面的示例程序中的通用类就需要两个通用数据类型:
//这个示例程序中用到了两个通用数据类型
#include<iostream>
using namespace std;
template<class T1, class T2> class MyClass
{
T1 i;
T2 j;
public:
MyClass(T1 a, T2 b)
{
i = a;
j = b;
}
void show()
{
cout << i <<' ' << j << "\n";
}
};
int main()
{
MyClass<int,double> ob1(10,0.23);
MyClass<char,char *> ob2('X',"This is a test");
ob1.show();//输出整型数,双精度浮点数的值
ob2.show();//输出字符,字符串的值
};
上面程序的输出结果如下:
10 0.23
X This is a test
上面的程序中声明了两种类型的对象:ob1对象使用的是整型和双精度浮点数类型;ob2对象使用的是字符和字符指针类型。针对这两种情况,编译器自动地生成正确的数据和函数来适应这两个对象。
显示地生成特定的类
和前面学习过的通用函数一样,我们也可以显示地指定通用类的特定实例。我们可以通过类似于显示生成通用函数的特定实例那样使用template<>的形式来创建通用类的特定实例。例如:
//显示指定通用类的实例
#include<iostream>
using namespace std;
template<class T> class MyClass
{
T x;
public:
MyClass(T a)
{
cout <<"Inside generic MyClass.\n";
x = a;
}
T getX()
{
return x;
}
};
//显示地生成通用类的实例
template<>class MyClass<int> //该类就是通用类MyClass的一个显示实例
{
int x;
public:
MyClass(int a)
{
cout <<"Inside MyClass<int> specialization.\n";
x = a * a;
}
int getX()
{
return x;
}
};
int main()
{
MyClass<double> d(10.1);
cout <<"double: " << d.getX() <<"\n";
MyClass<int> i(5); //这里用到的是显示实例
cout <<"int: " << i.getX() << "\n";
return 0;
}
上面程序的输出如下:
Inside generic MyClass.
double: 10.1
Inside MyClass<int> specialization.
int: 25
注意上面程序中显示指定通用类的实例的方式:
template<>class MyClass<int>
{
};
上面的写法告诉编译器将显示地生成针对int类型的MyClass通用类的实例。这种形式可用于针对任何通用类进行显示实例化。
显示地对通用类进行实例化使得我们可以针对个别的特殊的情况进行特殊处理,而其他情况下都是由编译器根据通用类自动生成相应代码。这无疑是对通用类的一种扩展。当然,如果我们发现我们需要处理的特殊情况很多,那此时我们就应该考虑使用通用类是否合适了。
练习:
1. 声明通用函数或者通用类的时候使用哪个关键字?
2. 通用函数是否可以被显示地重载?
3. 在使用通用类的时候,他的所有成员函数是否都会被自动地生成呢?