正在加载……
专注、离线、切勿分心
模板:
     模板是一种通用的描述机制,也就是说,使用模板允许使用通用类型来定义函数或类等,在使用时,通用类型可被具体的类型,如int、double甚至是用户自定义的类型来代替。模板引入一种全新的编程思维方式,称为“泛型编程”或“通用编程”。



为什么要定义模板:

1、模板的的引入大大简化了程序代码量,维持了结构的清晰,大大提高了程序设计的效率。该过程称为“类型参数化”。
2、强类型程序设计中,参与运算的所有对象的类型在编译时即确定下来,并且编译程序将进行严格的类型检查。为了解决强类型的严格性和灵活性的冲突。
(1)带参数宏定义(原样替换)
(2)重载函数(函数名相同,函数参数不同)
(3)模板(将数据类型作为参数)



模板的定义与实例化:

模板的引入使得函数定义摆脱了类型的束缚,代码更为高效灵活。模板有函数模板类模板之分。通过参数实例化构造出具体的函数或类,称为模板函数模板类
函数模板实际上不是个完整的函数定义,因为其中的类型参数还不确定,只是定义了某些类型的角色(或变量)在函数中的操作形式,因此,必须将模板参数实例化才能使用函数,用模板实例化后的函数也称为模板函数.
1、模板参数实例化才能使用函数,用模板实例化后的函数也称为模板函数
2、分为隐式实例化显式实例化(模板特化)
template <class T>
template<typename T>

模板不能分成头文件和实现文件,模板的声明和实现必须要在同一个文件里面; 在 .cpp 文件实现模板,要在 main 所在文件 #include  ".cpp"



函数模板:

函数模板的定义:
template <模板参数表>
返回类型 函数名(参数列表)
{
        //函数体
}

模板参数表不能为空,模板参数有两种类型:
classtypename修饰的类型参数,代表一种类型;
非类型参数表达式,必须是int类型,使用已知类型符,代表一个常量


#include <iostream>
using namespace std;
//非类型参数只能是int, 模板又提供了一种传递常量的方式
template <typename T,int num>       //函数模板声明
T func (T a);
int main()
{
        //cout<<func(4)<<endl<<endl;    不能再用隐式实例化
        //只能显示实例化
        cout<<func <int ,4>(3) <<endl;
        return 0;
}
//函数模板的实现
//要求:函数模板的声明和实现必须放在同一个文件里
template <typename T ,int num>
T func (T a)
{
        return a * num;
}
template_init.cc
//函数模板的实现
//要求:函数模板的声明和实现必须放在同一个文件里面
#include <iostream>
using namespace std;
template <typename T, int num>
T func(T a)
{
        return a * num;
}

// 模板不能分成头文件和实现文件,
// 模板的声明和实现必须要在同一个文件里面
#include "template_int.cc"
int main()
{
        cout << func<int, 4>(3) << endl;
}



函数模板的使用:
   1、函数模板的使用规则和普通函数是相同的,在使用函数模板之前,必须对函数模板进行声明,此声明必须在外部进行,也就是说不能在任何一个函数(包括main函数)中声明,声明的格式为:
 template <class T1[,class T2,……]>
 函数原型
   2、和普通函数一样,如果在使用函数模板前对函数模板进行了定义,函数模板的声明可以省略。
#include <iostream>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
        cout<<"template <> add()"<<endl;
        return a+b;
}
int main()
{
        cout<<add(10,20)<<endl;      //隐式实例化
        cout<<add(1.2,2.3)<<endl;
        return 0;
}
#include <iostream>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
        cout<<"template <> add()"<<endl;
        return a+b;
}
//模板特化(不能独立存在,先有通用的形式,才有特化的形式),优先执行
template <>
double add <double> (const double &a,const double &b)
{
        cout<<"template <> add <double> ()"<<endl;
        return a+b;
}
int main()
{
        cout<<add(10,20)<<endl;    //隐式实例化
        cout<<add(1.2,2.3)<<endl;
        return 0;
}





函数模板重载:

    函数模板支持重载,既可以模板之间重载(同名模板),也可以实现模板和普通函数间的重载,但模板的重载相比普通函数的重载要复杂一点
#include <iostream>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
        cout<<"template <> add()"<<endl;
        return a+b;
}
//普通函数跟函数模板发生重载时,会优先调用普通函数
int add(const int &a,const int &b)
{
        cout<<"int add(const int &,const int &)"<<endl;
        return a+b;
}
int main()
{
        cout<<add(10,20)<<endl;      //隐式实例化
        cout<<add(1.2,2.3)<<endl;
        return 0;
}
//函数模板和函数模板发生重载
#include<iostream>
using namespace std;
template<class T>
T max1(T a,T b)  // 为什么max1,因为max和std里面有重名
{
        cout<<"template<>max(T,T)"<<endl;
        return a>b?a:b;
}
template<class T>
T max1(T a,T b,T c)
{
        cout<<"template<>max(T,T,T)"<<endl;
        return a>b?(a>c?a:c):(b>c?b:c);
}
int main()
{
        int a=1,b=2,c=3;
        double a1=1.1,b2=2.2;
        cout<<max1(a,b,c)<<endl;
        cout<<max1(a1,b2)<<endl;
        return 0;
}
#include<iostream>
using std::cout;
using std::endl;
// 函数模板和数组的重载
template<class T>
T max(T a,T b)
{
        cout<<"max(T,T)"<<endl;
        return a>b?a:b;
}
template<class T>
//T max(T *a,T b)  // T max(T a[],T b)  // 都是错的
T max(T* a,int b)
{
        cout<<"max(T*,T)"<<endl;
        T tmp = 1>>sizeof(T);
        for(int i=1;i!=b;++i)
        {
                tmp = tmp>a[i]?tmp:a[i];
        }
        return tmp;
}
int main()
{
        int a[] = {1,2,6,4,5};
        int a1 = 1,a2 = 2;
        cout<<max(a1,a2)<<endl;
        cout<<max(a,sizeof(a)/sizeof(int))<<endl;
        return 0;
}



优先级与执行顺序:

  总体来说,一般函数优先于模板函数的执行
#include <iostream>
#include <string.h>
using namespace std;
//定义函数模板
template <typename T>
T add(const T &a,const T &b)
{
        cout<<"template <> add()"<<endl;
        return a+b;
}
#if 0
//模板特化,实现两个字符串相加
template <>
char * add <char *> (char * const &a,  char * const &b)
{
        cout<<"template <> add <char *> ()"<<endl;
        char * tmp = new char[strlen(a)+strlen(b)+1];
        strcpy(tmp,a);
        strcat(tmp,b);
        return tmp;
}
#endif
#if 1
//重载普通函数,实现两个字符串相加
char * add(char * const &a,char * const &b)
{
        cout<<"char * add(char * const &,char * const &)"<<endl;
        char * tmp = new char[strlen(a)+strlen(b)+1];
        strcpy(tmp,a);
        strcat(tmp,b);
        return tmp;
}
#endif
int main()
{
        cout<<add(10,20)<<endl;  //隐式实例化
        cout<<add(1.2,2.3)<<endl;
        char * p1 = new char [6];
        strcpy(p1,"hello");
        char * p2 = new char [6];
        strcpy(p2,"world");
        char * p3 = add(p1,p2);
        cout<<p3<<endl;
        return 0;
}


                   

类模板:

  类模板名<T>  对象名;    eg: Stack<int, 10> stack;
  ************************类模板参数可以给默认值,但函数模板不能给默认值。如果出现非类型参数,则必须是int型。

栈:

#include <iostream>
#include<string>
#include<sstream>
using namespace std;
template <typename T,int num>
class Stack
{
        private:
                int _top;
                T _parr[num];
        public:
                Stack();
                ~Stack();
                bool full();
                bool empty();
                bool push(T elem);
                bool pop(T &);
                int & getPos()
                {
                        return _top;
                }
};

template <typename T,int num>
Stack<T,num>::Stack():_top(-1)
{}
template <typename T,int num>
Stack<T,num>::~Stack()
{}
template <typename T,int num>
bool Stack<T,num>::full()
{
        return _top == (num-1);
}
template <typename T,int num>
bool Stack<T,num>::empty()
{
        return _top == -1;
}
template <typename T,int num>
bool Stack<T,num>::push(T elem)
{
        if(!full())
        {
                _parr[++_top] = elem;
                return true;
        }
        return false;
}
template <typename T,int num>
bool Stack<T,num>::pop(T & t)
{
        if(!empty())
        {
                t = _parr[_top--];
                return true;
        }
        else
                return false;
}
int test0(void)
{      
        Stack<int, 10> stackInt;
        cout << "开始时stakcInt是否为空?" << stackInt.empty() << endl;
        stackInt.push(5);
        cout << "此始时stakcInt是否为空?" << stackInt.empty() << endl;

        for(int idx = 1; idx !=10; ++idx)
        {
                stackInt.push(idx);
        }
        cout << "此时stakcInt是否已满?" << stackInt.full() << endl;

        for(int idx = 0; idx != 10; ++idx)
        {
                int elem = 0;
                stackInt.pop(elem);
                cout << elem << " ";
        }
        cout << endl;
        return 0;
}
int test1(void)
{      
        Stack<string, 10> stackInt;
        cout << "开始时stakcInt是否为空?" << stackInt.empty() << endl;
        stackInt.push("aa");
        cout << "此始时stakcInt是否为空?" << stackInt.empty() << endl;

        for(int idx = 1; idx !=10; ++idx)
        {
                string s(2, 'a' + idx);  
//string类的一个构造函数,表示含有2个元素的string对象,其中每个元素都初始化为后面的字符
                stackInt.push(s);
        }
        cout << "此时stakcInt是否已满?" << stackInt.full() << endl;

        for(int idx = 0; idx != 10; ++idx)
        {
                string elem;
                stackInt.pop(elem);
                cout << elem << " ";
        }
        cout << endl;
        return 0;
}
int main()
{
        test0();
        test1();
        return 0;
}


队:

#include<iostream>
#include<string>
using namespace std;
template<class T,int num>
class queue
{
        public:
                queue();
                ~queue();
                bool empty();
                bool full();
                bool push(T elem);
                bool pop(T& tmp);
                int size();
        private:
                int _front;
                int _real;
                T _arr[num];
};
template<class T,int num>
queue<T,num>::queue():_front(0),_real(0){}
template<class T,int num>
queue<T,num>::~queue(){}
template<class T,int num>
bool queue<T,num>::empty()
{
        return _front == _real;
}
template<class T,int num>
bool queue<T,num>::full()
{
        return _front == (_real+1)%num;
}
template<class T,int num>
bool queue<T,num>::push(T elem)
{
        if(!full())
        {
                _arr[_real] = elem;
                _real = (_real+1)%num;
                return true;
        }
        else
                return false;
}
template<class T,int num>
bool queue<T,num>::pop(T &tmp)
{
        if(!empty())
        {
                tmp = _arr[_front];
                _front = (_front+1)%num;
                return true;
        }
        else
                return false;
}
template<class T,int num>
int queue<T,num>::size()
{
        return (_real-_front+num)%num;
}
int main()
{
        queue<int,10> q1;
        q1.push(3);
        q1.push(5);
        int tmp;
        cout<<q1.size()<<endl;
        q1.pop(tmp);
        cout<<tmp<<endl;
        cout<<"----------------------"<<endl;
        queue<string,5> q2;
        q2.push("hello");
        q2.push("world");
        cout<<q2.size()<<endl;
        string tmpString;
        q2.pop(tmpString);
        cout<<q2.size()<<"  "<<tmpString<<endl;
        return 0;
}



函数成员模板:

可以将函数模板作为另一个类(必须是模板类)的成员,称为函数成员模板,其用法和普通成员函数类似

#include <iostream>
using namespace std;
template <typename T>
class Test
{
        public:
                template <typename D>
                        T convert(D);
};
//有些编译器成员函数模板实现放在类外不能编译通过
template <typename T>
template <typename D>
T Test<T>::convert(D t)
{
        return T(t);   //类型转换
}
int main()
{
        Test<int> test;
        cout<<test.convert(3.14)<<endl;  //隐式实例化
        cout<<test.convert<double>(3.14)<<endl;  // 显式实例化
        return 0;
}



嵌套模版类的模版类:

类模板不等于类定义,需要实例化或特化来生成类实例
● 模板的套嵌可以理解成在另外一个模板里面定义一个模板。以模板(类,或者函数)作为另一个模板(类,或者函数)的成员,也称成员模板
成员模版是不能声明为virtual的
#include <iostream>
using namespace std;
template <typename T,typename T1>
class Outside
{
        public:
        template <typename D>class Inside;    //模板类的前向声明
        private:
                Inside<T1> t;
        public:
                Outside(T1 x):t(x)
                {}
                void disp();
                template <typename D>
                class Inside
                {
                        private:
                                D r;
                        public:
                                Inside(D x):r(x){};
                                void disp();
                };
};
template <typename T,typename T1>
void Outside<T,T1>::disp()
{
        cout<<"Outside()"<<endl;
        t.disp();
}

template <typename T,typename T1>
template <typename D>
void Outside<T,T1>::Inside<D>::disp()
{
        cout<<"Inside()"<<endl;
        cout<<Outside<T,T1>::Inside<D>::r<<endl;
}

int main()
{
        Outside<int,double>::Inside<double> obj(3.5);  //类外定义嵌套类,要求嵌套类是public
        obj.disp();

        Outside<int,double> obj1(2.1);       //类外定义嵌套类,要求嵌套类是public;
        obj1.disp();
        return 0;
}
//模板类的成员函数也可以在定义外实现
//但必须是在所有类定义的外边,不能放在Outside内Inside外去实现.



模版做参数:

模板包含类型参数(如class Type)和非类型参数(如int NUM,NUM是常量),实际上,模板的参数可以是另一个模板
template<template <class T1> class T2, class T3,int Num>;

#include <iostream>
using namespace std;
template <typename T,int num>
class A
{
        private:
                T num1;
        public:
                A(T a=num):num1(a)
                {
                        cout<<"A()"<<endl;
                }
                int ReturnNum();
};
template <typename T,int num>
int A<T,num>::ReturnNum()
{
        return num1;
}
template <template<typename T,int num> class B,  typename T1,  int N> 
//第一个参数声明的是一个模板类
void disp()
{
        cout<<"disp()"<<endl;
        B<T1,N> ob;
        cout<<ob.ReturnNum()<<endl;
}
int main()
{
        disp<A,int,8>();    //第一个参数用模板类A来实例化
        return 0;
}


模板是C++引入的新特性,也是标准模板库STL的基础,模板有函数模板和类模板之分,两种应用有很多相似之处。学习模板,最重要的是理解模板定义(函数模板定义、类模板定义)与具体定义(函数定义和类定义)的不同,模板不是定义,要通过实例化(通过模板)或特化(避开模板)来生成具体的函数或类定义,再调用函数或创建类的对象。

模板支持嵌套,这就是说可以在一个模板里面定义另一个模板。以模板(类,或者函数)作为另一个模板(类,或者函数)的成员,也称成员模板。同时,模板也可以作为另一个模板的参数,出现在类型参数表中。

posted on 2018-05-05 08:55  正在加载……  阅读(176)  评论(0编辑  收藏  举报