zoukankan      html  css  js  c++  java
  • C++之模板

    C++模版分为函数模版和类模版。

    事实上模版就是一种对类型进行參数化的工具。


     

    一、函数模板

    1. 函数模板的声明定义

    Template<typename/class T1,typename/class T2,….>

    返回类型  函数名(函数參数){

         函数体

    }

    比如:

    template<typename T>
    T add(T t,T t1)
    {
           return t+t1;
    }

    2. 模板函数的两种调用方式

    (1)隐式调用

           int i=1,j=2;
           int k=add(i,j);

    默认是add函数的两个參数是同一类型。系统依据实际调用的时候的实參的类型来确定形參的详细类型。注意这里的两个參数类型必须同样。

    不能够:

           int i=1,j=2;
           int k=add(1,2.2);//error

    (2)显示调用

           int i=1,j=2;
           int k=add<int>(i,j);

    首先指定函数模板的详细类型。再去调用。

    这样就能够:

           int k=add<int>(1,2.3);//OK
    这里和隐式调用的差别:显示调用就是先指定函数模板的详细类型,则函数就变成了详细类型的函数了。intadd(int t,int t1)则,再去调用add<int>(1,2.3);显然就没有问题了。仅仅是系统进行了一下类型转换而已。

    二、类模板

    1. 类模板的声明定义

    Template<typename/class T1, typename/class T2,….>

    Class 类名{

    类的成员

    }

    比如:

    template<typename T>
    class A
    {
    public:A(T b):a(b){}
              T swap(T& i,T& j){
                     T temp;
                     temp=i;
                     i=j;
                     j=temp;
              }
              void foo(Tt);
    private: T a;
    };
    template<typename T>
    void A<T>::foo(T t)
    {cout<<t<<endl;}

    (1) 注意类模板的成员函数,假设在类外实现。必须在实现的前面加上template<…>。

    并且函数名前加”类名<T1[,T2,…]>”。

     

    (2)当在类中。想要使用自身类的对象时(比如:copy构造、operator=)时,直接使用类名就能够了。比如:

    class A
    {
    public:A(T b):a(b){}
              A(const A&c){
                     a=c.a;
              }
    private: T a;
    };

    (3)当在类外。想要使用这个类名的时候。必需要“类名+<T,….>”。比如成员函数的类外实现的时候。函数名前加的东西。

    class A
    {
    public:A(T b):a(b){}
              void foo(Ac);
    private: T a;
    };
    template<typename T>
    void A<T>::foo(A<T>t)
    {cout<<t<<endl;}

    (4)有时在类中。想使用同一模板的其它类型的类的对象。

    比如:

    class A
    {
    public:A(T b):a(b){}
              T getm(){returna;}
              template<typename Y>
              voidfoo(A<Y>& c){
                     cout<<c.getm()<<endl;
              }
    private: T a;
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
           A<int> a(2);
           A<double> b(3);
           a.foo(b);
           system("pause");
           return 0;
    }


    在函数的前面加template<typenameY>,注意这里的Y不能和类模板时的T同样。

     

    这样的情况经常在重写copy和operator=时。使用子类的对象给父类的对象copy构造、赋值时。会经经常使用到这个。详细可见auto_ptr的源代码。

    2. 类模板的调用

             A<int>a(2);

    定义类对象时,先声明类的详细类型。

    (1) 和函数模板隐式调用不一样的地方。能够:

    template<typename T>
    class A
    {
    public:A(T b):a(b){}
              T add(T t1, T t2)
              {returnt1+t2;}
    private: T a;
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
           A<int> a(2);
           a.add(2,3.4);//OK
           system("pause");
           return 0;
    }

    这里注意类模板的原理:因为类模板在定义的时候。已经确定了T的类型,所以在定义了A<int> a(2)之后,a中的T都变成了int,所以这里add函数就变成了int add(int t1,int t2).这显然就能够add(2,3.4)调用了。符合系统的默认类型转换。

    三、非类型形參

    当template<typenameT,int In>。

    出现了int这种详细类型的时候。In就是非类型形參。比如:

    template<typename T,int In>
    class A
    {
    public:A(){a=new T[In];a[0]=3;}
              void foo()
              {cout<<a[0]<<endl;}
              ~A(){delete[]a;}
    private: T* a;
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
           A<int,4> a;
           a.foo();
           system("pause");
           return 0;
    }

    1. 注意非类型形參In在内部是常量值,在上面代码中也能够看出。

    2. 非类型形參仅仅能是整型(包含int。short。unsigned int),指针和引用

    就是说不能够是float/double/string,但能够是double*/double&/对象的指针、引用也是能够的。

    class B{};
    template<typename T,doubleIn>class A{};//ERROR
    template<typename T,floatIn>class A{};//ERROR
    template<typename T,B In>classA{};//ERROR
    template<typename T,double&In>class A{};//OK
    template<typename T,double*In>class A{};//OK
    template<typename T,B* In>classA{};//OK

    3. 调用非类型形參的实參必须是一个常量表达式

    编译时能计算出结果。(注意sizeof的返回值也是常量,能够使用)

    注意:何为常量表达式?(值不会改变,且在编译阶段就能知道确切值的)

    const int 、常数、枚举。全局变量的引用/地址、全局对象的引用/地址。

    不为常量表达式:

    局部变量。局部对象,局部对象的地址/引用。全局变量。全局对象都不是常量表达式。

     

    总结:首先非类型的形參仅仅能是整型,指针,引用。

    这也决定了实參也必须仅仅能是这些,除此之外。实參还必须是一个常量表达式。所以这样一汇总:实參仅仅能是const int、常数、枚举、全局变量的引用/地址、全局对象的引用/地址。

    也能够:

    template<int* T>
    class A{};
    int d[]={2};
    int _tmain(int argc, _TCHAR* argv[])
    {
           A<d>a;//----------OK
           system("pause");
           return 0;
    }

    4. 有非类型形參的模板在类外实现成员函数时:

    事实上对于全部类型的类模板。在类外面定义类的成员时template后面的模板形參应与要定义的类的模板形參一致

    template<int* a>
    class A{
    public:void foo();
    };
    template<int* a>  -----在类外不要随便改动a的名称。保持和定义时一致
    void A<a>::foo(){}

    四、能够为类模板提供类型的默认值

     

    template<typename T=int>
    class A{};
    int _tmain(int argc, _TCHAR* argv[])
    {
           A<>a;//OK
           A<double> b;//OK
           system("pause");
           return 0;
    }

    1. 注意能够为类模板提供类型默认值。不能够为函数模板提供类型默认值。

    template<typename T=int>//ERROR
    void foo(){}

    2. 注意当有多个类型形參,则从第一个提供默认值的形參后面的形參,都要提供默认值。

     

    template<typename T=int,typename T1>//ERROR
    class A{};

    五、模板的特化

    1. 模板的特化就是给一个已存在的模板进行特殊类型时,运行特别的操作。

     

    template<typename T>
    class A{
    public:void foo(){cout<<"T"<<endl;}
    };
    template<>//模板的特化。当T为int时。运行的是这个,当其它类型时。运行的是上面
    class A<int>{
    public:void foo(){cout<<"int"<<endl;}
    };
    int _tmain(int argc, _TCHAR* argv[])
    {
           A<int> a;//运行的是以下那个特化模板
           a.foo();
           A<double> b;//运行的是上面那个普通模板
           b.foo();
           system("pause");
           return 0;
    }

    2. 模板的偏特化,也叫部分特化。

    当模板有多个形參时。对当中一部分进行特化。与上面的所有特化想相应。

    //1. 标准模板类。

    template<typename T1, typenameT2> class MyClass { ... ... }; //2. 两个模板參数具有同样类型的部分特化类。 template<typename T> class MyClass<T,T> { ... ... }; //3. 第二个类型參数是int template<typename T> class MyClass<T,int> { ... ... }; //4. 两个模板參数都是指针。 template<typename T1,typenameT2> classMyClass<T1*,T2*> { ... ... }; //5. 两个模板參数都是同样类型的指针。

    template<typename T> class MyClass<T*,T*>{ ... ... }; //6. 模板的所有特化,上面2~5为部分特化 template<> class MyClass<int,double>{ ...... }; int _tmain(int argc, _TCHAR* argv[]) { MyClass<int,float>c1; //调用MyClass<T1,T2> MyClass<float,float> c2; //调用MyClass<T,T> MyClass<float,int> c3; //调用MyClass<T,int> MyClass<int*,float*>c4; //调用MyClass<T1*,T2*> MyClass<int*,int*>c5; //调用MyClass<T*,T*> MyClass<int,double>c6; //调用MyClass<int,double> system("pause"); return 0; }



    所有特化一般都是template<>。中括号中没有东西。把所有的形參所有特化。

    部分特化一般都是template<T,…>。

    中括号中有东西,没有把全部的形參都特化。

     

    函数模板同理也能够特化。

    六、函数模板的重载

    类模板是不能够重载的。而函数模板能够重载。

    样例:

    template<typename T>
    void foo(T)
    {cout<<"template"<<endl;}
    void foo(int)
    {cout<<"foo"<<endl;}
    int _tmain(int argc, _TCHAR* argv[])
    {
           int i=2;
           double j=2.0;
           foo(i);//调用的是foo(int)------(1)
           foo(j);//调用的是template<>foo(T)----------(2)
           system("pause");
           return 0;
    }


    和特化类似,仅仅是类模板不能够重载。(废话,类能重载吗)

    重载选择的顺序:“越特殊越优先高”

    显然假设(1)这么写的话:”foo<int>(i);”就是显式的调用函数模板,这样就调用的tempalte<>foo(T);了。

  • 相关阅读:
    环境部署(二):Linux下安装jenkins
    环境部署(一):Linux下安装JDK
    jenkins简单安装及配置(Windows环境)
    python相关资料链接
    性能测试相关资料链接
    管理篇:测试Leader应该做哪些事
    进阶篇:软件测试工程师的岗位职责
    基础篇:如何做一名专业的软件测试工程师
    python:面向对象编程之Zope.interface安装使用
    Django:Python3.6.2+Django2.0配置MySQL
  • 原文地址:https://www.cnblogs.com/mthoutai/p/6742835.html
Copyright © 2011-2022 走看看