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来代替。
  • 偏特化即对模板类型做一些限制,偏特化分为两种,部分特化和类型范围限制
  1. 部分特化就是只对类模板的一部分模板类型进行特化:
//比如原本是
template <class a, class b>
class test{}
//部分特化后为
template <class a>
class test<a,char>{}
  1. 类型范围限制,主要的类型基础不变
//比如原本是
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值