17. C++类模板
2019-04-11 本文已影响5人
飞扬code
在C++中我们往往需要编写多个形式和功能都相似的类,于是 引人了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复工作。
17.1 类模板基本格式
C++ 中类模板的写法如下:
template <类型参数表>
class 类模板名{
成员函数和成员变量
};
类型参数表的写法如下:
class类塑参数1, class类型参数2, ...
类模板中的成员函数放到类模板定义外面写时的语法如下:
template <类型参数表>
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{
...
}
用类模板定义对象的写法如下:
类模板名<真实类型参数表> 对象名(构造函数实际参数表);
如果类模板有无参构造函数,那么也可以使用如下写法:
类模板名 <真实类型参数表> 对象名;
类模板看上去很像一个类。下面以 Pair 类模板为例来说明类模板的写法和用法。
实际中,某项数据记录由两部分组成,一部分是关键字,另一部分是值。关键字用来对记录进行排序和检索,根据关键字能查到值。如,学生记录由两部分组成,一部分是学号,另一部分是绩点。要能根据学号对学生进行排序,以便方便地检索绩点,则学号就是关键字,绩点就是值。
例如:
#include <iostream>
#include <string>
using namespace std;
template <class T1,class T2>
class Pair
{
public:
T1 key; //关键字
T2 value; //值
Pair(T1 k,T2 v):key(k),value(v) { }
bool operator < (const Pair<T1,T2> & p) const;
};
template<class T1,class T2>
bool Pair<T1,T2>::operator < (const Pair<T1,T2> & p) const
//Pair的成员函数 operator <
{ //"小"的意思就是关键字小
return key < p.key;
}
int main()
{
Pair<string,int> student("哈哈",23); //实例化出一个类 Pair<string,int>
cout << student.key << " " << student.value;
return 0;
}
![](https://img.haomeiwen.com/i16823531/842be975afba0a41.png)
17.2 函数模板作为类模板成员
类模板中的成员函数还可以是一个函数模板。成员函数模板只有在被调用时才会被实例化。例如下面的程序:
#include <iostream>
using namespace std;
template <class T>
class A
{
public:
template <class T2>
void func(T2 t) { cout << t; } //成员函数模板
};
int main()
{
A<int> a;
a.func('K'); //成员函数模板Func被实例化
a.func("hello");
return 0;
}
17.3 C++用类模板实现可变长数组
#include <iostream>
#include <cstring>
using namespace std;
template <class T>
class Array
{
int size; //数组元素的个数
T *ptr; //指向动态分配的数组
public:
Array(int s = 0); //s代表数组元素的个数
Array(Array & a);
~Array();
void push_back(const T & v); //用于在数组尾部添加一个元素v
Array & operator=(const Array & a); //用于数组对象间的赋值
T length() { return size; }
T & operator[](int i)
{//用以支持根据下标访问数组元素,如a[i] = 4;和n = a[i]这样的语句
return ptr[i];
}
};
template<class T>
Array<T>::Array(int s):size(s)
{
if(s == 0)
ptr = NULL;
else
ptr = new T[s];
}
template<class T>
Array<T>::Array(Array & 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>
Array<T>::~Array()
{
if(ptr) delete [] ptr;
}
template <class T>
Array<T> & Array<T>::operator=(const Array & a)
{ //赋值号的作用是使"="左边对象里存放的数组,大小和内容都和右边的对象一样
if(this == & a) //防止a=a这样的赋值导致出错
return * this;
if(a.ptr == NULL) { //如果a里面的数组是空的
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 Array<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()
{
Array<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;
}
![](https://img.haomeiwen.com/i16823531/684df16bf95bae96.png)
注意:
1、类模板中可以定义静态成员,从该类模板实例化得到的所有类都包含同样的静态成员。
#include <iostream>
using namespace std;
template <class T>
class A
{
private:
static int count;
public:
A() { count ++; }
~A() { count -- ; }
A(A &) { count ++ ; }
static void PrintCount() { cout << count << endl; }
};
/*
下面也可以这样写
int A<int>::count = 0;
int A<double>::count = 0;
*/
template<> int A<int>::count = 0;
template<> int A<double>::count = 0;
int main()
{
A<int> ia;
A<double> da;
ia.PrintCount();
da.PrintCount();
return 0;
}
![](https://img.haomeiwen.com/i16823531/341bc5161cc95981.png)
2、类模板的“类型参数表”中可以出现非类型参数
#include <iostream>
using namespace std;
template<class T, int size>
class Array {
T array[size];
public:
void Print()
{
for (int i = 0; i < size; ++i)
cout << array[i] << endl;
}
};
int main()
{
return 0;
}
可以用 CArray 模板定义对象
Array<int, 40> a;
编译器自动生成名为 CArray<int, 40> 的类。该类是通过将 CArray 模板中的 T 换成 int、 size 换成 40 后得到的。
还可定义以下对象:
Array <double, 40> a2;
Array <int, 50> a3;
注意,Array<double, 40> 和 Array<int, 50> 完全是两个类,这两个类的对象之间不能互相赋值。