- 函数模板定义的一般形式如下: 
    template <typename 类型参数1, typename 类型参数2,...> 返回值类型 函数名(形参列表){
        // 在函数体中可以使用累成参数
    }
         - typename: 一个关键字,用来声明具体的类型参数。typename关键字也可以使用class关键字替代,它们没有任何区别。C++早期对模板的支持并不严谨,没有引入新的关键字,而是用 class来指明类型参数,但是class关键字本来已经用在类的定义中了,这样做显得不太友好,所以后来C++又引入了一个新的关键字 typename,专门用来定义类型参数。不过至今仍然有很多代码在使用class关键字,包括C++标准库、一些开源程序等。     
 - template: 是定义函数模板的关键字,后面紧跟尖括号<>     
 -  类型参数(也可以说是虚拟的类型,或者说是类型占位符): 模板头中包含的类型参数可以用在函数定义的各个位置,包括返回值、形参列表和函数体。类型参数的命名规则跟其他标识符的命名规则一样,不过使用T、T1、T2、Type等已经成为了一种惯例。     
 - template<typename T>: 模板头    
 
     - 函数模板实例1如下: 
    #include <iostream>
    
    using namespace std;
    // 函数模板
    template<typename T> void Swap(T &a, T &b){  // 此处的tyname关键字可以改成class!
        T temp;
        temp = a;
        a = b;
        b = temp;
    }
    int main(){
        // int类型变量交换
        int n1 = 200, n2 = 100;
        Swap(n1, n2);
        cout << "n1 = " << n1 << ", n2 = " << n2 << endl;
        // float类型变量交换
        float f1 = 3.14, f2 = 5.99;
        Swap(f1, f2);
        cout << "f1 = " << f1 << ", f2 = " << f2 << endl;
        return 0;
    }
     - 函数模板实例2如下: 
    #include <iostream>
    using namespace std;
    // 函数模板的声明
    template<class T> T max(T a, T b, T c);
    int main(){
        // 求三个整数的最大值
        int i1, i2, i3, i_max;
        cout << "请依次输入三个整数: \n";
        cin >> i1 >> i2 >> i3;
        i_max = max(i1, i2, i3);
        cout << "i_max = " << i_max << endl;
        cout << "----------------------------------------------\n";
        // 求三个浮点数的最大值
        double d1, d2, d3, d_max;
        cout << "请依次输入三个浮点数: \n";
        cin >> d1 >> d2 >> d3;
        d_max = max(d1, d2, d3);
        cout << "d_max = " << d_max << endl;
        cout << "----------------------------------------------\n";
        return 0;
    }
    // 定义函数模板
    template<class T>   // 模板头,不能有分号
    T max(T a, T b, T c){  // 函数头
        T max_num = a;
        if( b > max_num)
            max_num = b;
        if(c > max_num)
            max_num = c;
        return max_num;
    }
     - 函数模板实例3如下: 
    #include <iostream>
    #include "string"
    using namespace std;
    // 函数模板实例3
    template<class T> inline T const& Max(T const &a, T const &b){
        return a < b ? b:a;
    }
    int main(){
        int i = 20, j = 30;
        cout << "Max(i, j) = " << Max(i, j) << endl;
        double db1 = 23.4, db2 = 44.9;
        cout << "Max(db1, db2) = " << Max(db1, db2) << endl;
        string s1 = "hello", s2 = "world";
        cout << "Max(s1, s2) = " << Max(s1, s2) << endl;
        return 0;
    }
    
     - C++除了支持函数模板,还支持类模板(Class Template)。函数模板中定义的类型参数可以用在函数声明和函数定义中,类模板中定义的类型参数可以用在类声明和类实现中,类模板的目的同样是将数据的类型参数化。一但声明了类模板,就可以将类型参数用于类的成员函数和成员变量了。换句话说,原来使用int、float、char等内置类型的地方,都可以用类型参数来代替。   
 - 声明类模板的语法为: 
    template <typename 类型参数1, typename 类型参数2,...> class 类名{
        //TODO;
    }
     - 类模板中类的声明如下: 
    template<typename T1, typename T2>  // 这里不能有分号!!!
    class Point{
        public:
            Point(T1 x, T2 y): m_x(x), m_y(y){
                cout << "构造函数\n";
            }
        public:
            T1 getX() const;  // 常函数
            void setX(T1 x);  
            T2 getY() const;
            void setY(T2 y);
        private:
            T1 m_x;
            T2 m_y;
    };
     - 上面的代码仅仅是类的声明,我们还需要在类外定义成员函数。在类外定义成员函数时仍然需要带上模板头,格式为: 
    template<typename 类型参数1, typename 类型参数2,...> 返回值类型 类名<类型参数1, 类型参数2,...>::函数名(形参列表){
        TODO;
    }
     - 类模板中的成员函数定义如下: 
    // 类外成员函数的定义
    template<typename T1, typename T2>  // 模板头
    T1 Point<T1, T2>::getX() const{  // 函数头
        return m_x;
    } 
    template<typename T1, typename T2>
    void Point<T1, T2>::setX(T1 x){
        m_x = x;
    }
    template<typename T1, typename T2>
    T2 Point<T1, T2>::getY() const{
        return m_y;
    }
    template<typename T1, typename T2>
    void Point<T1, T2>::setY(T2 y){
        m_y = y;
    }
         - 仔细观察代码,除了 template 关键字后面要指明类型参数,类名Point后面也要带上类型参数,只是不加typename关键字了。另外需要注意的是,在类外定义成员函数时,template后面的类型参数要和类声明时的一致。    
 
    
     - 上面的两段代码完成了类的定义,接下来就可以使用该类创建对象了。使用类模板创建对象时,需要指明具体的数据类型。请看下面的代码: 
    Point<int, int> p1(10, 20);
    Point<int, float> p2(10, 5.5);
    Point<float, char*> p3(4.3, "hello");
     -  与函数模板不同的是,类模板在实例化时必须显式地指明数据类型,编译器不能根据给定的数据推演出数据类型。   
 - 除了对象变量,我们也可以使用对象指针的方式来实例化: 
    Point<float, float> *p1 = new Point<float, float>(10.6, 90.3);
    Point<char*, char*> *p2 = new Point<char*, char*>("hello", "world");
     -  需要注意的是,赋值号两边都要指明具体的数据类型,且要保持一致。下面的写法是错误的: 
    // 赋值号两边的数据类型不一致
    Point<float, float> *p = new Point<float, int>(10.6, 109);
    // 赋值号右边没有指明数据类型
    Point<float, float> *p = new Point(10.6, 109);
     - 综合实例1如下: 
    #include <iostream>
    using namespace std;
    template<class T1, class T2>  //这里不能有分号
    class Point{
    public:
        Point(T1 x, T2 y): m_x(x), m_y(y){ }
    public:
        T1 getX() const;  //获取x坐标
        void setX(T1 x);  //设置x坐标
        T2 getY() const;  //获取y坐标
        void setY(T2 y);  //设置y坐标
    private:
        T1 m_x;  //x坐标
        T2 m_y;  //y坐标
    };
    template<class T1, class T2>  //模板头
    T1 Point<T1, T2>::getX() const /*函数头*/ {
        return m_x;
    }
    template<class T1, class T2>
    void Point<T1, T2>::setX(T1 x){
        m_x = x;
    }
    template<class T1, class T2>
    T2 Point<T1, T2>::getY() const{
        return m_y;
    }
    template<class T1, class T2>
    void Point<T1, T2>::setY(T2 y){
        m_y = y;
    }
    int main(){
        Point<int, int> p1(10, 20);
        cout<<"x="<<p1.getX()<<", y="<<p1.getY()<<endl;
    
        Point<int, char*> p2(10, "东经180度");
        cout<<"x="<<p2.getX()<<", y="<<p2.getY()<<endl;
    
        Point<char*, char*> *p3 = new Point<char*, char*>("东经180度", "北纬210度");
        cout<<"x="<<p3->getX()<<", y="<<p3->getY()<<endl;
        return 0;
    }
     - 类模板综合实例2如下: 
    template <class T>
    class CArray
    {
    private:
        int size; // 数组的大小
        T *ptr;   // 指向动态数组的指针
    public:
        CArray(int s = 0); // 构造函数
        CArray(CArray &a); // 拷贝构造函数
        ~CArray();         // 析构函数
        void push_back(const T &v);         //用于在数组的尾部添加一个元素
        CArray &operator=(const CArray &a); // 运算符重载,用于数组对象间的重载
        // 注意下面的两个函数已经定义过了
        T length()
        {
            return size;
        }
        T &operator[](int i)
        {
            return ptr[i]; // 用以支持根据下标访问数组元素
        }
    };
    // 类成员函数的定义
    template <class T>
    CArray<T>::CArray(int s) : size(s)
    {
        if (s == 0)
            ptr = NULL;
        else
            ptr = new T[s];
    }
    template <class T>
    CArray<T>::CArray(CArray &a)
    {
        if (!a.ptr)
        {
            ptr = NULL;
            size = 0;
            return;
        }
        ptr = new T[a.size];
        memcpy(ptr, a, ptr, sizeof(T) * a.size);
        size = a.size;
    }
    template<class T>
    CArray<T>::~CArray(){
        if(ptr)
            delete [] ptr;
    }
    template <class T>
    CArray<T> &CArray<T>::operator=(const CArray &a)
    {
        if (this == &a) //防止a=a这样的赋值导致出错, 赋值号的作用是使"="左边对象里存放的数组,大小和内容都和右边的对象一样
            return *this;
        if (a.ptr == NULL)
        { // 如果a里面的数组是空的
            if (ptr)
                delete[] ptr;
            ptr = NULL;
            size = 0;
            return *this;
        }
        if (size < a.size)
        { // 如果原有空间够大,就不用分配新的空间
            if (ptr)
                delete[] ptr;
            ptr = new T[a.size];
        }
        memcpy(ptr, a.ptr, sizeof(T) * a.size);
        return *this;
    }
    template <class T>
    void CArray<T>::push_back(const T &v)
    { //在数组尾部添加一个元素
        if (ptr)
        {
            T *tmpPtr = new T[size + 1];           // 重新分配空间
            memcpy(tmpPtr, ptr, sizeof(T) * size); // 拷贝原数组内容
            delete[] ptr;
            ptr = tmpPtr;
        }
        else // 数组本来是空的
            ptr = new T[1];
        ptr[size++] = v; // 加入新的数组元素
    }
    int main()
    {
        CArray<int> a;
        for(int i =0; i < 5;i++)
            a.push_back(i);
        for(int i=0; i < a.length(); i++)
            cout << "a[" << i << "] = " << a[i] << endl;
        return 0;
    }