C++ 除了支持函数模板,还支持类模板(Class
Template)。函数模板中定义的类型参数可以用在函数声明和定义中,类模板中定义的类型参数可以用在类声明和实现中。类模板的目的同样是将数据的类型参数化。
声明类模板的语法为:
| 12
 3
 
 | template<typename 类型参数1, typename 类型参数2, ...> class 类名 {//
 }
 
 | 
类模板和函数模板都是以 template 开头(当然也可以使用
class,目前来讲它们没有任何区别),后跟类型参数;类型参数不能为空,多个类型参数用逗号隔开。
一旦声明了类模板,就可以将类型参数用于类的成员函数和成员变量了。换句话说,原来使用
int、float、char 等内置类型的地方,都可以用类型参数来代替。
假如我们现在要定义一个类来表示坐标,要求坐标的数据类型可以是整数、小数和字符串,例如:
这个时候就可以使用类模板,请看下面的代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | template<typename T1, typename T2> class Point {
 public:
 Point(T1 x, T2 y): m_x(x), m_y(y) {}
 public:
 T1 getX() const;
 void setX(T1 x);
 T2 getY() const;
 void setY(T2 y);
 private:
 T1 m_x;
 T2 m_y;
 }
 
 | 
x 坐标和 y
坐标的数据类型不确定,借助类模板可以将数据类型参数化,这样就不必定义多个类了。
注意:模板头和类头是一个整体,可以换行,但是中间不能有分号。
上面的代码仅仅是类的声明,我们还需要在类外定义成员函数。在类外定义成员函数时仍然需要带上模板头,格式为:
| 12
 3
 4
 
 | template<typename 类型参数1, typename 类型参数2, ...>返回值类型 类名<类型参数1, 类型参数2, ...>::函数名(形参列表) {
 //
 }
 
 | 
下面就对 Point 类的成员函数进行定义:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | template<typename T1, typename T2> T1 Point<T1, T2>::getX() const  {
 return m_x;
 }
 
 template<typename T1, typename T2>
 void Point<T1, T2>::setX(T1 x) {
 m_x = x;
 }
 
 template<typename T1, typename T2>
 T2 Point<T1, T2>::getY() const {
 return m_y;
 }
 
 template<typename T1, typename T2>
 void Point<T1, T2>::setY(T2 y) {
 m_y = y;
 }
 
 | 
请读者仔细观察代码,除了 template 关键字后面要指明类型参数,类名
Point 后面也要带上类型参数,只是不加 typename
关键字了。另外需要注意的是,在类外定义成员函数时,template
后面的类型参数要和类声明时一致。
使用类模板创建对象
上面的两段代码完成了类的定义,接下来就可以使用该类创建对象了。使用类模板创建对象时,需要指明具体的数据类型。请看下面的代码:
| 12
 3
 
 | Point<int, int> p1(10, 20);Point<int, float> p2(10, 15.5);
 Point<float, char*> p3(12.4, "东经180度");
 
 | 
与函数模板不同的是,类模板在实例化时必须显式地指明数据类型,编译器不能根据给定的数据推演出数据类型。
除了对象变量,我们也可以使用对象指针的方式来实例化:
| 12
 
 | Point<float, float> *p1 = new Point<float, float>(10.6, 109.3);Point<char*, char*> *p = new Point<char*, char*>("东经180度", "北纬210度");
 
 | 
需要注意的是,赋值号两边都要指明具体的数据类型,且要保持一致。下面的写法是错误的:
| 12
 3
 4
 
 | Point<float, float> *p = new Point<float, int>(10.6, 109);
 
 Point<float, float> *p = new Point(10.6, 109);
 
 | 
综合示例
将上面的类定义和类实例化的代码整合起来,构成一个完整的示例,如下所示:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 
 | #include<iostream>using namespace std;
 
 template<class T1, class T2>
 class Point{
 public:
 Point(T1 x, T2 y): m_x(x), m_y(y) {}
 public:
 T1 getX() const;
 void setX(T1 x);
 T2 getY() cosnt;
 void setY(T2 y);
 private:
 T1 m_x;
 T2 m_y;
 };
 
 template<class T1, class T2>
 T1 Point<T1, T2>::getX() const  {
 retrun m_x;
 }
 
 template<class T1, class T2>
 void Point<T1, T2>::setX(T1 x) {
 m_x = x;
 }
 
 template<class T1, class T2>
 T2 Point<T1, T2>::getY() const {
 return m_y;
 }
 
 template<class T1, class T2>
 void Point<T1, T2>::setY(T2 y) {
 m_y = y;
 }
 
 int main() {
 Point<int, int> p1(10, 20);
 cout << "x=" << p1.getX() << ", y=" << p1.getY() << endl;
 
 Point<int, char*> p2(10, "东经180度");
 cout << "x=" << p2.getX() << ", y=" << p2.getY() << endl;
 
 Point<char*, char*> *p3 = new Point<char*, char*>("东经180度", "北纬210度");
 cout << "x=" << p3->getX() << ", y=" << p3->getY() << endl;
 
 return 0;
 }
 
 | 
运行结果:
| 12
 3
 
 | x=10, y=20x=10, y=东经180度
 x=东经180度, y=北纬210度
 
 | 
在定义类型参数时,我们使用了 class,而不是
typename,这样做的目的是让读者对两种写法都熟悉。
用类模板实现可变长数组:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 
 | #include<iostream>#include<cstring>
 using namespace std;
 
 template<class T>
 class CArray
 {
 int size;
 T *ptr;
 
 public:
 CArray(int s = 0);
 CArray(CArray &a);
 ~CArray();
 void push_back(const T &v);
 CArray & operator=(const CArray &a);
 int length() { return size; }
 T & operator[](int i )
 {
 
 return ptr[i];
 }
 };
 
 template<class T>
 CArray<T>::CArray(int s): size(s)
 {
 if (s == 0)
 ptr = NULL;
 else
 ptr = new T[s];
 }
 
 template<class T>
 CArray<T>::CArray(CArray &a)
 {
 if (!a.ptr) {
 ptr = NULL;
 size = 0;
 return;
 }
 ptr = new T[a.size];
 memcpy(ptr, a.ptr, sizeof(T) * a.size);
 size = a.size;
 }
 
 template<class T>
 CArray<T>::~CArray()
 {
 if(ptr) delete []ptr;
 }
 
 template<class T>
 CArray<T> & CArray<T>::operator=(const CArray &a)
 {
 
 if (this == &a)
 return *this;
 if (a.ptr == NULL) {
 
 if (ptr)
 delete []ptr;
 ptr = NULL;
 size = 0;
 return *this;
 }
 if (size < a.size) {
 
 if (ptr)
 delete []ptr;
 ptr = new T[a.size];
 }
 
 memcpy(ptr, a.ptr, sizeof(T) * a.size);
 size = a.size;
 return *this;
 }
 
 template<class T>
 void CArray<T>::push_back(const T &v)
 {
 
 if (ptr) {
 T *tmpPtr = new T[size + 1];
 memcpy(tmpPtr, ptr, sizeof(T) * size);
 delete []ptr;
 ptr = tmpPtr;
 } else
 ptr = new T[1];
 ptr[size++] = v;
 }
 
 int main()
 {
 CArray<int> a;
 for (int i = 0; i < 5; i++) {
 a.push_back(i);
 }
 for (int i = 0; i < a.length(); ++i)
 cout << a[i] << " ";
 return 0;
 }
 
 |