C++模板
函数模板
函数模板是通用的函数描述,使用泛型来定义函数,其中的泛型可用具体的类型替换。通过将类型作为参数传递个模板,使得编译器为其自动生成该类型的函数。
模板函数又可以分为一般模板函数和特化模板函数
一般模板函数
一般函数模板的写法如下:
template <class 类型参数1, class 类型参数2,……>
返回值类型 模板名(形参数)
{
函数体
}
其中 class可以和typenane关键字相互替换,作用相同。
例如下面的程序:
template<class Type>
void Queue<Type>::push(const Type& val){
QueueItem<Type> * pt = new QueueItem<Type>(val);
if(empty()){
head = tail = pt; //如果对象queueitem是空的,则为其生成一个队列,头为当前的queue对象
}else{ //如果对象不为空,则在其队列的后面加上此queue对象
tail->next = pt;
tail = pt;
}
}
假设其参数为int类型,编译器则会识别并生成如下函数:
void Queue::push(const int& val){
QueueItem * pt = new QueueItem<(val);
if(empty()){
head = tail = pt; //如果对象queueitem是空的,则为其生成一个队列,头为当前的queue对象
}else{ //如果对象不为空,则在其队列的后面加上此queue对象
tail->next = pt;
tail = pt;
}
}
特化模板函数
为什么要有特化模板函数呢?
例如:当编写了一个函数判断是否相等时,int型的可以用=来简单判断,而char、string类型的则需要用strcmp来判断。也可以通俗地理解为,对编译器来说,特化模板函数做得比一般模板函数更好,自然而然会选择特化模板函数。
- 特化模板函数定义的前提是要现有一般模板函数
- 特化模板函数除了类型、函数体和一般模板函数不同,其它均需要相同,避免不必要的错误
- 特化模板函数的格式:
template <>
返回值 函数名<特化类型>(特化类型 参数1, 特化类型 参数2, ……)
{
函数体
}
类模板
类模板于函数模板的定义和使用类似,也是为了定义多个仅仅只是类型不同的类。
类模板的声明格式如下:
template<模板参数表>
class 类名
{
类成员声明
}
模板参数表的关键字依旧是class和typename。
例如下面的程序定义了一个类模板:
template<class Type>
class QueueItem{ //定义偏特化模板类queueitem
QueueItem(const Type &t):item(t), next(0){} //构造函数,初始化item、next,&t是为了使传入变量完整
Type item;
QueueItem * next;
friend class Queue<Type>; //定义友元类queue,可以访问queueitem的成员-++
friend ostream& operator<<(ostream& os, const Queue<Type> &q);
public:
QueueItem<Type>* operator++(){
return next;
}
Type & operator*(){
return item;
}
};
如果需要在类模板以外定义其成员函数,则要采用一下格式:
template<模板参数表>
类型名 类名<模板参数识别符列表>::函数名(参数表)
类模板特化
类模板也有特化,分为两种,一种是全特化,一种是偏特化。
- 全特化即将所有模板类型都进行特化,例如将上述程序的Type用int来代替。
- 偏特化即对模板类型做一些限制,偏特化分为两种,部分特化和类型范围限制。
- 部分特化就是只对类模板的一部分模板类型进行特化:
//比如原本是
template <class a, class b>
class test{}
//部分特化后为
template <class a>
class test<a,char>{}
- 类型范围限制,主要的类型基础不变
//比如原本是
template <class a, class b>
class test{}
//部分特化后为
template <class a, class b>
class test<a*, b*>{}
在调用的时候都将调用与自己类型最相匹配的。
模板成员函数特化
在上面的程序中,类中的成员函数都是沿用了类模板的参数,但是在类模板中也可以定义单独的函数模板,这样不仅类是可变的,其中的成员函数也是可变的,大大增强了程序的多变性。例如下方程序中的成员函数就换了参数。
template<class Type> class Queue{ //定义偏特化模板类queue
public:
Queue():head(0),tail(0){}
Queue(const Queue& q):head(0),tail(0){
copy_items(q);
}
template<class It> //定义模板函数,改变变量类型为it
Queue(It beg, It end):head(0),tail(0){copy_items(beg,end);}
template<class It> void assign(It beg, It end);
Queue& operator=(const Queue&);
~Queue(){destroy();}
Type& front() {return head->item;}
const Type& front() const{return head->item;}
void push(const Type &);
void pop();
bool empty() const{return head==0;}
friend ostream& operator<<(ostream& os, const Queue<Type> &q){
os<<"< ";
QueueItem<Type> * p;
for(p=q.head;p;p=p->next){
os<<p->item<<" ";
}
os<<">";
return os;
}
const QueueItem<Type>* Head() const{return head;} //返回头部
const QueueItem<Type>* End() const{return (tail==NULL)?NULL:tail->next;} //递归返回尾部
private:
QueueItem<Type> * head;
QueueItem<Type> * tail;
void destroy();
void copy_items(const Queue &);
template<class It> void copy_items(It beg,It end);
};
其中的destroy、push、pop成员函数在类外进行重写
template<class Type>
void Queue<Type>::destroy() //清空对象的数据
{
while (!empty()) {
pop();
}
}
template<class Type>
void Queue<Type>::pop(){ //删除队列的最后一个
QueueItem<Type> * p = head;
head = head->next;
delete p;
}
template<class Type>
void Queue<Type>::push(const Type& val){
QueueItem<Type> * pt = new QueueItem<Type>(val);
if(empty()){
head = tail = pt; //如果对象queueitem是空的,则为其生成一个队列,头为当前的queue对象
}else{ //如果对象不为空,则在其队列的后面加上此queue对象
tail->next = pt;
tail = pt;
}
}
可以看出,重写的类型也是和类的参数类型一样的,这样的成员函数还可以特化成以下形式:
template<>
void Queue<const char*>::push(const char * const &val){
char* new_item = new char[strlen(val)+1];
strncpy(new_item,val,strlen(val)+1);
QueueItem<const char*> * pt = new QueueItem<const char*>(new_item);
if(empty()){
head=tail=pt;
}else{
tail->next = pt;
tail = pt;
}
}
template<>
void Queue<const char*>::pop(){
QueueItem<const char*> * p = head;
delete head->item;
head = head->next;
delete p;
}
这样就将对字符类型参数的函数进行单独的操作了。
这里有一个小技巧。
在.cpp中重写上述函数实现时,如果不对.h中的函数声明动点手脚的话,会报错,错误类型为多次声明了该成员函数,可以在.h文件里的函数返回类型前加上inline或者static解决,解决原理就是不让其产生外部符号,因此在.cpp引用.h文件时不会被其引用。
template<> //全特化函数,定义当为字符类型时的函数模板
inline void Queue<const char*>::push(const char * const &val);
template<>
inline void Queue<const char*>::pop();
queue实现
queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include<string.h>
#include <iostream>
using namespace std;
template<class Type> class Queue; //声明偏特化模板类queue
template<class Type> class QueueItem{ //定义偏特化模板类queueitem
QueueItem(const Type &t):item(t), next(0){} //构造函数,初始化item、next,&t是为了使传入变量完整
Type item;
QueueItem * next;
friend class Queue<Type>; //定义友元类queue,可以访问queueitem的成员-++
friend ostream& operator<<(ostream& os, const Queue<Type> &q);
public:
QueueItem<Type>* operator++(){
return next;
}
Type & operator*(){
return item;
}
};
template<class Type> class Queue{ //定义偏特化模板类queue
public:
Queue():head(0),tail(0){}
Queue(const Queue& q):head(0),tail(0){
copy_items(q);
}
template<class It> //定义模板函数,改变变量类型为it
Queue(It beg, It end):head(0),tail(0){copy_items(beg,end);}
template<class It> void assign(It beg, It end);
Queue& operator=(const Queue&);
~Queue(){destroy();}
Type& front() {return head->item;}
const Type& front() const{return head->item;}
void push(const Type &);
void pop();
bool empty() const{return head==0;}
friend ostream& operator<<(ostream& os, const Queue<Type> &q){
os<<"< ";
QueueItem<Type> * p;
for(p=q.head;p;p=p->next){
os<<p->item<<" ";
}
os<<">";
return os;
}
const QueueItem<Type>* Head() const{return head;} //返回头部
const QueueItem<Type>* End() const{return (tail==NULL)?NULL:tail->next;} //递归返回尾部
private:
QueueItem<Type> * head;
QueueItem<Type> * tail;
void destroy();
void copy_items(const Queue &);
template<class It> void copy_items(It beg,It end);
};
template<class Type>void Queue<Type>::destroy() //清空对象的数据
{
while (!empty()) {
pop();
}
}
template<class Type> void Queue<Type>::pop(){ //删除队列的最后一个
QueueItem<Type> * p = head;
head = head->next;
delete p;
}
template<class Type> void Queue<Type>::push(const Type& val){
QueueItem<Type> * pt = new QueueItem<Type>(val);
if(empty()){
head = tail = pt; //如果对象queueitem是空的,则为其生成一个队列,头为当前的queue对象
}else{ //如果对象不为空,则在其队列的后面加上此queue对象
tail->next = pt;
tail = pt;
}
}
template<> //全特化函数,定义当为字符类型时的函数模板
inline void Queue<const char*>::push(const char * const &val);
template<>
inline void Queue<const char*>::pop();
template<class Type>
void Queue<Type>::copy_items(const Queue &orig){ //复制一个和传入的queueitem对象一样的队列
for(QueueItem<Type> *pt = orig.head;pt;pt=pt->next){
push(pt->item);
}
}
template<class Type>
Queue<Type>& Queue<Type>::operator=(const Queue& q) //重载=,使其能有copy_item函数的功能
{
destroy();
copy_items(q);
}
template<class Type> template<class It> void Queue<Type>::assign(It beg, It end)
{
destroy();
copy_items(beg,end);
}
template<class Type> template<class It> void Queue<Type>::copy_items(It beg,It end)
{
while(beg!=end){
push(*beg);
++beg;
}
}
//template<> class Queue<const char*>{
//public:
// void push(const char* str){real_queue.push(str);}
// void pop(){real_queue.pop();}
// bool empty() const {return real_queue.empty();}
// string front() {return real_queue.front();}
// const string &front() const{return real_queue.front();}
//private:
// Queue<string> real_queue;
//};
template<class Type>
int compare(const Type& v1, const Type& v2)
{
if(v1<v2) return -1;
if(v1>v2) return 1;
return 0;
}
template<>
int compare<const char*>(const char * const &v1, const char * const &v2);
#endif // QUEUE_H
queue.cpp
#include "queue.h"
#include <string.h>
// template<>
// int compare<const char*>(const char * const &v1, const char * const &v2)
// {
// return strcmp(v1,v2);
// }
template<>
void Queue<const char*>::push(const char * const &val){
char* new_item = new char[strlen(val)+1];
strncpy(new_item,val,strlen(val)+1);
QueueItem<const char*> * pt = new QueueItem<const char*>(new_item);
if(empty()){
head=tail=pt;
}else{
tail->next = pt;
tail = pt;
}
}
template<>
void Queue<const char*>::pop(){
QueueItem<const char*> * p = head;
delete head->item;
head = head->next;
delete p;
}
test_queue.h
#ifndef TEST_QUEUE_H
#define TEST_QUEUE_H
#include "queue.h"
#include<vector>
void TestQueue()
{
Queue<int> qt;
double d = 3.3;
qt.push(1);
qt.push(d); //问题:模板函数并没有生成double类型的push
qt.push(10);
cout<<endl;
cout<<qt;
short a[5] = {0,3,6,9};
Queue<int> qi(a,a+5);
cout<<endl;
cout<<qi;
vector<int> vi(a,a+5);
qi.assign(vi.begin(),vi.end());
cout<<endl;
cout<<qi;
Queue<const char*> q1;
q1.push("hi");
q1.push("I'm");
q1.push("qkl");
cout<<endl;
cout<<q1;
Queue<const char*> q2(q1);
cout<<q2;
}
// #include "handle.h"
// #include "CMatrix.h"
// void TestAutoPtr()
// {
// AutoPtr<CMatrix> h1;
// double data[6] = {1,2,3,4,5,6};
// h1->Create(2,3,data);
// AutoPtr<CMatrix> h2(h1);
// (*h2).Set(0,1,10);
// cout<<*h1<<*h2;
// }
#endif // TEST_QUEUE_H
main.cpp
#include"test_queue.h"
#include"queue.cpp"
int main()
{
TestQueue();
return 0;
}
AutoPtr实现
AutoPtr.h
#ifndef AUTOPTR_H
#define AUTOPTR_H
template <class T>
class AutoPtr
{
public:
//构造函数
AutoPtr(T* pData);
//拷贝构造函数
AutoPtr(const AutoPtr<T>& h);
//声明周期结束时调用析构函数
~AutoPtr();
//等号的重载
AutoPtr<T>& operator=(const AutoPtr<T>& h);
//用户数减1
void decrUser();
//指针运算符重载(返回的指针允许被改变)
T* operator ->() {
return m_pData;
}
//返回一个对象(能使用成员运算符(".")来访问成员变量)
T& operator*() {
return *m_pData;
}
const T& operator *() const {
return *m_pData;
}
//指针运算符重载(返回的指针不允许被改变)
const T* operator -> () const {
return m_pData;
}
private:
//存储数据
T* m_pData;
//存储用户数
int* m_nUser;
};
template < class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
//初始化用户数为1
m_nUser = new int(1);
}
template < class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h) {
m_pData = h.m_pData;
m_nUser = h.m_nUser;
//用户数加1
(*m_nUser)++;
}
template < class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
template < class T>
void AutoPtr<T>::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0) {
//删除数据
delete m_pData;
//地址赋为空
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}
template < class T>
AutoPtr<T>::~AutoPtr()
{
decrUser();
}
#include"CMatrix.h"
void TestAutoPtr()
{
//创建一个CMatrix类的指针并交给智能指针类进行管理
AutoPtr<CMatrix> h1(new CMatrix);
double data[6] = {1,2,3,4,5,6};
//生成一个2行3列的数组
h1 -> Create(2, 3, data);
cout << *h1 << endl;
//h2(拷贝构造函数的使用)和h1指向的是同一个地方
AutoPtr<CMatrix> h2(h1);
(*h2).Set(0, 1, 10);
cout << *h1 << *h2 << endl;
}
#endif // AUTOPTR_H
主函数调用
#include"test_queue.h"
#include"queue.cpp"
#include"AutoPtr.h"
int main()
{
// TestQueue();
TestAutoPtr();
return 0;
}