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   >
        >

 

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