前面我们介绍了函数模板,今天我们来介绍类模板,与函数模板的作用类似,编译器从类模板可以自动生成多个类,避免了程序员的重复劳动。
1.类模板的定义:
(1)C++ 中类模板的写法如下:
template <类型参数表>
class 类模板名{
成员函数和成员变量;
};
类型参数表的写法如下:
class类塑参数1, class类型参数2, ...(class 也可用typename替换)
(2)类模板中的成员函数放到类模板定义外面写时的语法如下:
template <类型参数表>
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表)
{
...
}
(3)用类模板定义对象的写法如下:
类模板名<真实类型参数表> 对象名(构造函数实际参数表);
如果类模板有无参构造函数,那么也可以使用如下写法:
类模板名 <真实类型参数表> 对象名;
2.类模板的使用
下面是一个链表的类模板举例:
template<typename T>
class CLink;
template<typename T>
class Node
{
public:
Node(T val = T()) :mdata(val), pnext(NULL){}
//Node(const Node<T>& rhs)
//~Node(){}
private:
T mdata;
Node<T>* pnext;
//template<typename E>
//friend class CLink;
friend class CLink<T>;
};
template<typename T>
class CLink
{
public:
CLink();
~CLink();
bool InsertHead(T val);
void Show();
private:
Node <T>* phead;
};
template<typename T>
CLink<T>::CLink()
{
phead = new Node<T>();
}
template<typename T>
CLink<T>::~CLink()
{
Node<T>* pCur = phead;
Node<T>* pNext = pCur;
while (pCur != NULL)
{
pNext = pCur->pnext;
delete pCur;
pCur = pNext;
}
phead = NULL;
}
template<typename T>
bool CLink<T>::InsertHead(T val)
{
Node<T>* pnewnode = new Node<T>(val);
pnewnode->pnext = phead->pnext;
phead->pnext = pnewnode;
return true;
}
template<typename T>
void CLink<T>::Show()
{
Node<T>* pCur = phead->pnext;
while (pCur != NULL)
{
std::cout << pCur->mdata << " ";
pCur = pCur->pnext;
}
std::cout << std::endl;
}
int main()
{
CLink<int> link;
for (int i = 0; i < 10; i++)
{
link.InsertHead(i + 1);
}
link.Show();
return 0;
}
下面是对上述类模板的选择性实例化和特例化:
template<typename T>
class CLink
{
public:
CLink()
{
std::cout << "template<typename T> class CLink" << std::endl;
phead = new Node();
}
CLink(const CLink<T>& rhs)//拷贝构造函数用一个已存在对象来生成一个相同类型的的新对象
{
std::cout << "CLink(const CLink<T>& rhs)" << std::endl;
phead = new Node();
Node* ptail = phead;
Node* pCur = rhs.phead->pnext;
while (pCur != NULL)
{
Node* pnewnode = new Node(pCur->mdata);
ptail->pnext = pnewnode;
pCur = pCur->pnext;
ptail = ptail->pnext;
}
}
template<typename E>
CLink(const CLink<E>& rhs)// /拷贝构造函数的模板退化成构造函数
{
std::cout << "template<typename E> CLink(const CLink<E>& rhs)" << std::endl;
phead = new Node();
Node* ptail = phead;
CLink<E>::Node* pCur = rhs.phead->pnext;
while (pCur != NULL)
{
Node* pnewnode = new Node(pCur->mdata);
ptail->pnext = pnewnode;
pCur = pCur->pnext;
ptail = ptail->pnext;
}
}
~CLink()
{
Node* pCur = phead;
Node* pNext = pCur;
while (pCur != NULL)
{
pNext = pCur->pnext;
delete pCur;
pCur = pNext;
}
phead = NULL;
}
bool InsertTail(T val)
{
Node* pnewnode = new Node(val);
Node* ptail = phead;
while (ptail->pnext != NULL)
{
ptail = ptail->pnext;
}
ptail->pnext = pnewnode;
return true;
}
void Show()
{
Node* pCur = phead->pnext;
while (pCur != NULL)
{
std::cout << pCur->mdata << " ";
pCur = pCur->pnext;
}
std::cout << std::endl;
}
class Node;
Node* Find(T val)//普通方法
{
std::cout <<"普通方法版本"<< "Node* CLink<T>::Find(T)" << std::endl;
Node* pCur = phead->pnext;
Node* prt = NULL;
while (pCur != NULL)
{
if (pCur->mdata == val)
{
prt = pCur;
break;
}
}
return prt;
}
template<typename E>
Node* Find(E val) //模板版本
{
std::cout <<"模板版本"<< "template<typename E> Node* CLink<T>::Find(E)" << std::endl;
Node* pCur = phead->pnext;
Node* prt = NULL;
while (pCur != NULL)
{
if (pCur->mdata == val)
{
prt = pCur;
break;
}
}
return prt;
}
template<>
Node* Find(char* val) //特例化的版本
{
std::cout <<"特例化版本"<<"template<> Node* CLink<T>::Find(char*)" << std::endl;
Node* pCur = phead->pnext;
Node* prt = NULL;
while (pCur != NULL)
{
if (strcmp(pCur->mdata,val) == 0)
{
prt = pCur;
break;
}
}
return prt;
}
private:
class Node
{
public:
Node(T val = T()) :mdata(val), pnext(NULL){}
public:
T mdata;
Node* pnext;
};
template<typename T>
friend class CLink;
Node* phead;
};
int main()
{
CLink<int> ilink;//CLink<int>构造"template<typename T> class CLink"
CLink<double> dlink(ilink);// 拷贝构造"template<typename E> CLink(const CLink<E>& rhs)"
CLink<int> ilink1(ilink);// 拷贝构造模板"CLink(const CLink<T>& rhs)"
return 0;
}
类模板中的成员函数还可以是一个函数模板,例如上述24~38行代码,成员函数模板只有在被调用时才会被实例化
注意:
(1)类模板构造函数和析构函数中不建议加模板类型参数,其他地方建议加上模板类型参数
(2)类模板是选择性实例化的,一定要在使用时指明实例化类型
(3)typename关键字作用:用在模板定义里,在模板形参列表里定义一个虚假类型,标明其后的模板参数是类型参数
(4)类模板特例化时,不用给整个类实现特例化,只要将其个别函数实现特例化就好
(5)拷贝构造函数的模板是构造函数
3.函数模板与类模板有什么区别?
答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化 必须由程序员在程序中显式地指定。
即函数模板允许隐式调用和显式调用而类模板只能显示调用这期间有涉及到函数模板与模板函数,类模板与模板类的概念(类似于类与类对象的区别)
注意:与函数模板相似,类模板的定义和实现必须写在一起,一般都在头文件中完成。C++中每一个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象的所占用的空间的大小的。只有模板被真正使用的时候,编译器才知道,模板套用的是什么类型,应该分配多少空间。这也就是模板类为什么只是称之为模板,而不是泛型的缘故。这一点与函数模板有些相似。
4.类模板与模板类;函数模板与模板函数
类模板的重点是模板,表示的是一个模板,专门用于产生类的模子;模板类的重点是类,表示的是由一个模板生成而来的
相似的,函数模板的重点是模板,表示的是一个模板,专门用来生产函数;模板函数的重点是函数。表示的是由一个模板生成而来的函数