C++泛型編程——模板學習

模板

模板使類和函數可在編譯時定義所需處理和返回的數據類型。

一個模板並非一個實實在在的類或函數,僅僅是一個類或函數的描述。

模板一般分爲模板函數類模板,以所處理的數據類型的說明作爲參數的類叫類模板,以所處理的數據類型的說明作爲參數的函數,稱爲函數模板。

模板的一般定義形式爲:

template <類型形參表> 返回類型 類名<類型名錶>::成員函數名1(形參表){成員函數1定義體}......

模板參數

模板的參數可以是某些類型type或者非類型nontype,類型參數可用typenameclass關鍵字指明。如:

template <class T1,class T2,class T3> class classname;

template <typename T1,typenameT2,typenameT3> class classname;

非類型參數就是通常的參數定義,表示在模板實例化爲類時所用的常量。如:

template<class type,int size【=默認值】> class Queue;

typename關鍵字告訴了編譯器把一個特殊的名字解釋成一個類型,在下列情況下必須對一個name使用typename關鍵字:

1、一個唯一的name,嵌套在另一個類型中。

2、依賴於一個模板參數,即模板參數在某種程序上包含這個name。當模板參數使編譯器在指認一個類型時便會產生誤解。

#include <iostream>
using namespace std;

class Y {
public:
    class id {   //Y類嵌套了id類
    public:
        void g(){cout<<"hello !"<<endl;}
    };
};

template<class T>
class X {
    typename T::id i;  //告訴編譯器 T::id 爲一個類型
public:
    void f(){i.g();}
};

int main() {
    Y y;
    X<Y> xy;
    xy.f();
    return 0;
}

運行結果:

hello !

爲了保險起見,在所以編譯器可能錯把一個type當成一個變量的地方使用typename。

函數模板

一般定義形式爲:

template <類型1 變量1,類型2 變量2,類型3 變量3  ......> 返回類型 函數名(形參表){函數定義體}

編譯器根據調用該函數實參數據類型,生成相應的重載函數,該重載函數稱爲模板函數,是一個實實在在的函數。

#include <iostream>
using namespace std;

template<class T>
T min(T ii,T jj,T kk)
{
    T temp;
    if((ii<jj)&&(ii<kk))
    {
        temp=ii;
    }else if((jj<ii)&&(jj<kk))
    {
        temp=jj;
    }else{
        temp=kk;
    }
    return temp;
}

int main() {
    cout<<min(100,20,30)<<endl;
    cout<<min(10.60,10.64,53.21)<<endl;
    cout<<min('c','a','C')<<endl;
    return 0;
}

運行結果:

20
10.6
C

使用函數模板與同名的非模板函數重載,就是函數的定製。定製須遵循以下規定:

1、尋找一個參數完全匹配的函數,找到了就調用它。

2、失敗,尋找一個函數模板,使其實例化,產生一個匹配的模板函數,找到就調用它。

3、失敗,再試低一級的對函數重載的方法,如通過類型轉換可產生的參數匹配等,找到調用它。

4、失敗,則報錯。

#include <iostream>
#include <string.h>
using namespace std;
//函數定製
//模板函數
template<class T>
T min(T ii,T jj,T kk)
{
    T temp;
    if((ii<jj)&&(ii<kk))
    {
        temp=ii;
    }else if((jj<ii)&&(jj<kk))
    {
        temp=jj;
    }else{
        temp=kk;
    }
    return temp;
}
//非模板函數
const char* min(const char* ch1, const char* ch2,const char* ch3)
{
    const char* temp;
    int result1=strcmp(ch1,ch2);
    int result2=strcmp(ch1,ch3);
    int result3=strcmp(ch2,ch1);
    int result4=strcmp(ch2,ch3);
    if((result1<0)&&(result2<0))
    {
        temp=ch1;
    } else if ((result3<0)&&(result4<0))
    {
        temp=ch2;
    } else{
        temp=ch3;
    }

    return temp;
}

int main() {
    cout<<min(100,20,30)<<endl;
    cout<<min(10.60,10.64,53.21)<<endl;
    cout<<min('c','a','C')<<endl;
    cout<<min("Anderson","Washington","Smith")<<endl;
    return 0;
}

運行結果:

20
10.6
C
Anderson

函數模板實例

字符串轉換系統:

#include <iostream>
#include <string>
#include <complex>

using namespace std;
//定義了一個函數模板,實現輸入功能
template <typename T>
T fromString(const string& s)
{
    istringstream is(s);
    T t;
    is>>t;
    return t;
}
//定義了一個函數模板,實現輸出功能
template <typename T>
string toString(const T& t)
{
    ostringstream s;
    s<<t;
    return s.str();
}

int main() {
    int i=1234;
    cout<<"i== \" "<<toString(i)<<" \"\n";
    float x=567.89;
    cout<<"x == \" "<<toString(x)<<" \"\n";
    complex<float> c(1.0,2.0);
    cout<<"c == \""<<toString(c)<<" \"\n";
    cout<<endl;
    i=fromString<int>(string("1234"));
    cout<<"i == "<<i<<endl;
    x=fromString<float>(string("567.89"));
    cout<<"x == "<<x<<endl;
    c=fromString<complex<float>>(string("(1.0,2.0)"));
    cout<<"c == "<<c<<endl;
    return 0;
}

運行結果:

i== " 1234 "
x == " 567.89 "
c == "(1,2) "

i == 1234
x == 567.89
c == (1,2)

內存分配系統:

#include <cstdlib>
#include <iostream>
#include <cstring>

using namespace std;
/*
 * 內存分配系統,分配的內存分佈如下
 * [[一個整型所佔的字節數存放分配的元素個數][分配的存放元素的內存,oldmem指針指向該地方]]
 */
template <class T>
//oldmem表示指針引用,elems表示需要分配的大小
void getmem(T*& oldmem,int elems)
{
    typedef int cntr;
    const int csz=sizeof(cntr); //獲取一個整型數據所佔的字節數
    const int tsz= sizeof(T);  //獲取一個元素數據所佔的字節數

    if(elems==0)  //當傳遞過來的參數爲0時,表示釋放掉該塊內存
    {
        //oldmem並沒有指向內存的開始空間,把內存的起始空間給了計數器使用,當我們需要釋放掉這塊內存時,需要倒退掉這個指針cntr所佔用的字節數.
        free(&(((cntr*)oldmem)[-1]));
        return;
    }

    T* p=oldmem;
    cntr oldcount=0;
    if(p)
    {
        cntr* tmp= reinterpret_cast<cntr*>(p); //將oldmem指針轉換成cntr指針
        p= reinterpret_cast<T*>(--tmp); //將tmp倒退一個cntr所佔用的字節數,再轉換成T類型指針
        oldcount=*(cntr*)p;//獲取P指針最開始的cntr所佔用字節數的數據,用作計數器用。
    }

    T* m=(T*)realloc(p,elems*tsz+csz); //調用realloc調整空間
    if(m==0) //判斷是否分配失敗
    {
        cout<<"ERROR!"<<endl;
        exit(1);
    }

    *((cntr*)m)=elems;//將需分配的元素個數填進新分配的空間的起始cntr所佔用字節數地方
    const  cntr increment=elems-oldcount;//獲取增加的空間
    if(increment>0)
    {
        long startadr=(long)&(m[oldcount]);
        startadr+=csz;
        memset((void*)startadr,0,increment*tsz);//將增加的內存以0填充
    }

    oldmem=(T*)&(((cntr*)m)[1]); //將分配的內存除掉計數器使用的內存塊賦給oldmem帶出
}

template <class T>
inline void freemem(T* m)
{
    getmem(m,0);  //傳遞的參數0,則表示釋放掉這塊內存。
}

int main() {
    int* p=0;

    getmem(p,10);
    for(int i=0;i<10;i++)
    {
        cout<<p[i]<<' ';
        p[i]=i;
    }
    cout<<'\n';

    getmem(p,20);
    for(int j=0;j<20;j++)
    {
        cout<<p[j]<<' ';
        p[j]=j;
    }
    cout<<'\n';

    getmem(p,25);
    for(int k=0;k<25;k++)
    {
        cout<<p[k]<<' ';
    }

    freemem(p);

    cout<<'\n';

    float* f=0;

    getmem(f,3);
    for(int u=0;u<3;u++)
    {
        cout<<f[u]<<' ';
        f[u]=u+3.14159;
    }
    cout<<'\n';

    getmem(f,6);
    for(int v=0;v<6;v++)
    {
        cout<<f[v]<<' ';
    }
    cout<<'\n';

    getmem(f,2);
    for(int v=0;v<2;v++)
    {
        cout<<f[v]<<' ';
        f[v]=v+0.1234;
    }
    cout<<'\n';

    getmem(f,8);
    for(int v=0;v<8;v++)
    {
        cout<<f[v]<<' ';
    }
    freemem(f);
    return 0;
}

運行結果:

0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 0 0 0 0 0
0 0 0
3.14159 4.14159 5.14159 0 0 0
3.14159 4.14159
0.1234 1.1234 0 0 0 0 0 0

類模板

一般定義形式爲:

template <類型形參表> class 類名 {類聲明體}

建立類模板之後,可以用類型實參定義模板類,並創建實例。

在類模板的使用中,需要注意以下幾個問題:

1、所以出現類模板的地方,不能直接用類名,需要在類名後面加上<>,指定模板類型

2、在類模板定義體中可省略<>

3、在一個類模板的各個實例之間沒有特殊的聯繫(形成一個個獨立的類)

4、模板的實例可用在一般類可以使用的任何地方,用模板實例定義的對象和一般的對象用法完全相同。

5、實例化的時機:在需要時實例化,比如定義指針或引用不需要實例化,定義具體的變量或常量時會實例化,而訪問對象的成員時會實例化。

類模板中的友元定義有很多情況,主要分以下幾種情形:

1、非模板函數、類成爲所以實例類的友元。

2、模板函數、模板類成爲同類型實例類的友元。

3、模板函數、類成爲不同類型實例類的友元。

類模板實例

#include <iostream>
#include <ostream>

using namespace std;

template <class Type> class Queue;
template <class Type> class QueueItem;
template <class Type> ostream& operator<<(ostream& os,const Queue<Type>& item);
template <class Type>
class Queue
{
public:
    Queue():front(NULL),back(NULL){}
    ~Queue();

    Type& remove();
    void add(const Type&);
    bool is_empty(){return front==NULL;}

protected:
    friend ostream& operator<<<>(ostream& os, const Queue<Type>& item);

private:
    QueueItem<Type> *front;
    QueueItem<Type> *back;
};

template <class Type>
void Queue<Type>::add(const Type& newItem)
{
    QueueItem<Type> *pNew=new QueueItem<Type>;
    pNew->item=newItem;
    pNew->nextItem=NULL;
    if(this->front==NULL)
    {
        this->front=this->back=pNew;
    } else{
        this->back->nextItem=pNew;
        this->back=pNew;
    }
}

template <class Type>
Queue<Type>::~Queue()
{
    QueueItem<Type> *p,*q;
    p=front;
    while(p!=NULL)
    {
        q=p->nextItem;
        delete p;
        p=q;
    }
}

template <class Type>
Type& Queue<Type>::remove()
{
    QueueItem<Type> *pNew=this->front;
    this->front=this->front->nextItem;

    return *pNew;
}

template <class Type>
ostream& operator<<(ostream& os, const Queue<Type>& item)
{
    QueueItem<Type>* pNew=item.front;
    while(pNew!=item.back)
    {
        os<<pNew->item<<"\t";
        pNew=pNew->nextItem;
    }
    os<<pNew->item<<endl;

    return os;
}

template <class Type>
class QueueItem
{
public:
    Type item;
    QueueItem* nextItem;
};

int main() {
    Queue<int> IntQueue;
    IntQueue.add(5);
    IntQueue.add(6);
    IntQueue.add(7);
    IntQueue.add(8);
    cout<<IntQueue<<endl;

    return 0;
}

運行結果:

5       6       7       8

#include <iostream>
#include <ostream>

using namespace std;

class A
{
    int j;
public:
    A(){}
    A(int x):j(x){}
    A(A* x){j=x->j;}
    void operator!(){cout<<"J="<<j<<endl;}
};

template <class T>
class B
{
    int i;
    T *X;

public:
    B(int xa,T *p):i(xa){X=new T(p);}

    void operator!(){cout<<"I="<<i<<endl;!*X;}
};

int main() {
    A a(1);
    B<A> b(2,&a);
    !b;

    return 0;
}

運行結果:

I=2
J=1
#include <iostream>

using namespace std;

class Student
{
    int number;
    static Student *ip;
    Student *p;

public:
    Student(){p=NULL;}
    Student(int n);
    static Student* get_first(){return ip;}
    int get_number(){return this->number;}
    Student* get_next(){return this->p;}
};

Student::Student(int n):number(n)
{
    p=NULL;
    if(ip==NULL)
    {
        ip=this;
    }else{
        Student *temp=ip;

        if(n<ip->number)
        {
            ip=this;
            p=temp;
        } else{
            while(temp)
            {
                if(n<temp->p->number)
                {
                    p=temp->p;
                    temp->p=this;
                    break;
                }else{
                    if(temp->p->p==NULL)
                    {
                        temp->p->p=this;
                        break;
                    }
                }
                temp=temp->p;
            }
        }
    }
}

Student* Student::ip=NULL;

template <class T>
class Class
{
    int num;
    T *p;

public:
    Class(){}
    Class(int n):num(n){p=NULL;}

    T* insert(int n){p=new T(n);return p;}

    void list_all_member(T* x)
    {
        T *temp=x;
        while(temp)
        {
            cout<<temp->get_number()<<",";
            temp=temp->get_next();
        }
    }
};


int main() {
    Class<Student> t_class(9707);
    t_class.insert(23);
    t_class.insert(12);
    t_class.insert(38);
    t_class.insert(22);
    t_class.insert(32);

    t_class.list_all_member(Student::get_first());

    return 0;
}

運行結果:

12,22,23,32,38,

模板安全

模板根據參數的類型進行實例化,因爲通常事先不知道其具體類型,所以也無法確切知道將在哪兒產生異常。

注意: 對構造函數或析構函數上的function-try-block,當控制權到達了異常處理函數的結束點時,被捕獲的異常被再次拋出。對於一般的函數,此時是函數返回,等同於沒有返回值的return語句,對於定義了返回類型的函數此時的行爲未定義。

類模板實例

擴展vector功能:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

template <class T>
class Sorted:public vector<T>
{
public:
    void sort();
};

template <class T>
void Sorted<T>::sort()
{
    for(int i=this->size();i>0;i--)
    {
        for(int j=1;j<i;j++)
        {
            if(this->at(j-1)>this->at(j))
            {
                T t=this->at(j-1);
                this->at(j-1)=this->at(j);
                this->at(j)=t;
            }
        }
    }
}


int main() {
    Sorted<int> is;
    for(int i=15;i>0;i--)
    {
        is.push_back(i);
    }

    for(int l=0;l<is.size();l++)
    {
        cout<<is[l]<<"\t";
    }
    cout<<endl;

    is.sort();
    for(int l=0;l<is.size();l++)
    {
        cout<<is[l]<<"\t";
    }
    cout<<endl;
    
    return 0;
}

運行結果:

15      14      13      12      11      10      9       8       7       6       5       4       3       2       1

1       2       3       4       5       6       7       8       9       10      11      12      13      14      15

list容器類設計:

#include <iostream>
#include <ostream>
#include <string>

using namespace std;

template <class T>
class ListElement
{
public:
    T data;
    ListElement<T>* next;
    ListElement(T& i_d,ListElement<T>* i_n):data(i_d),next(i_n){}
    ListElement<T>* copy()
    {
        return new ListElement(data,(next? next->copy() : 0));
    }
};

template <class T>
class ListIterator
{
public:
    T operator()();
    int operator++();
    int operator!();

    ListElement<T>* rep;
};

template <class T>
T ListIterator<T>::operator()()
{
    if(rep)
    {
        return rep->data;
    } else{
        T tmp;
        return tmp;
    }
}

template <class T>
int ListIterator<T>::operator++()
{
    if(rep)
    {
        rep=rep->next;
    }
    return (rep!=0);
}

template <class T>
int ListIterator<T>::operator!()
{
    return (rep!=0);
}

template <class T> class List;
template <class T>ostream& operator<<(ostream& os,const List<T>& l);
template <class T>
class List
{
public:
    friend class ListIterator<T>;
    List();
    List(const List&);
    ~List();

    List<T>& operator=(const List<T>&);

    T head();
    List<T> tail();
    void add(T&);

    friend ostream& operator<<<>(ostream& os,const List<T>& l);

public:
    void clear();
    ListElement<T>* rep;
};

template <class T>
List<T>::List()
{
    rep=0;
}

template <class T>
List<T>::List(const List<T>& l)
{
    rep=l.rep? l.rep->copy() : 0;
}

template <class T>
List<T>& List<T>::operator=(const List<T>& l)
{
    if(rep!=l.rep)
    {
        clear();
        rep=l.rep? l.rep->copy() : 0;
    }

    return *this;
}

template <class T>
List<T>::~List()
{
    clear();
}

template <class T>
void List<T>::clear()
{
    while(rep)
    {
        ListElement<T>* tmp=rep;
        rep=rep->next;
        delete tmp;
    }
    rep=0;
}

template <class T>
void List<T>::add(T& i)
{
    rep=new ListElement<T>(i,rep);
}

template <class T>
T List<T>::head()
{
    if(rep)
    {
        return rep->data;
    } else{
        T tmp;
        return tmp;
    }
}

template <class T>
List<T> List<T>::tail()
{
    List<T> tmp;
    if(rep)
    {
        if(rep->next)
        {
            tmp.rep=rep->next->copy();
        }
    }

    return tmp;
}

template <class T>
ostream& operator<<(ostream& os,const List<T>& l)
{
    if(l.rep)
    {
        ListElement<T>* p=l.rep;
        os<<"< ";
        while(p)
        {
            os<<p->data<<"\t";
            p=p->next;
        }
        os<<">\n";
    } else
    {
        os<<"Empty list\n";
    }

    return os;
}

int main() {
    List<int> l;
    cout<<l;
    int i=1;
    l.add(i);
    i=2;
    l.add(i);
    i=3;
    l.add(i);

    cout<<"l is "<<l<<endl;
    cout<<"head of l is "<<l.head()<<endl;
    List<int> m=l.tail();
    List<int> o;
    o=m;
    i=4;
    m.add(i);
    cout<<"m is "<<m<<endl;
    cout<<"o is "<<o<<endl;
    List<char> clist;
    char ch;
    ch='a';
    clist.add(ch);
    ch='b';
    clist.add(ch);
    cout<<clist<<endl;
    List<string> ls;
    string a("hello");
    string b("world");
    ls.add(a);
    ls.add(b);
    cout<<"List of strings"<<endl;
    cout<<ls<<endl;

    List<List<int>> listlist;
    listlist.add(o);
    listlist.add(m);
    cout<<"List of lists of ints\n";
    cout<<listlist<<endl;
    List<List<List<int>>> lllist;
    lllist.add(listlist);
    lllist.add(listlist);
    cout<<"List of lists of lists of ints\n";
    cout<<lllist<<"\n";
    List<List<string>> slist;
    slist.add(ls);
    slist.add(ls);
    cout<<"List of lists of strings\n";
    cout<<slist<<"\n";

    return 0;
}

運行結果:

Empty list
l is < 3        2       1       >

head of l is 3
m is < 4        2       1       >

o is < 2        1       >

< b     a       >

List of strings
< world hello   >

List of lists of ints
< < 4   2       1       >
        < 2     1       >
        >

List of lists of lists of ints
< < < 4 2       1       >
        < 2     1       >
        >
        < < 4   2       1       >
        < 2     1       >
        >
        >

List of lists of strings
< < world       hello   >
        < world hello   >
        >

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章