zoukankan      html  css  js  c++  java
  • 26.C++- 泛型编程之类模板(详解)

    在上章25.C++- 泛型编程之函数模板(详解) 学习了后,本章继续来学习类模板

     

    类模板介绍

    和函数模板一样,将泛型思想应用于类.

    编译器对类模板处理方式和函数模板相同,都是进行2次编译

    类模板通常应用于数据结构方面,使得类的实现不在关注数据元素的具体类型,而只关注需要实现的功能

    比如: 数组类,链表类,Queue类,Stack类等

    使用方法

    • 通过template关键字来声明,然后通过typename关键字来定义模板类型,如下图所示:

     

    类模板的使用

    • 定义对象时,必须指定类模板类型,因为编译器无法推导类型
    • 使用具体类型<Type>来定义对象

    如下图所示:

     

     

    初探类模板

    写一个类模板,实现不同类型的加减乘除

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template < typename T >
    class Operator
    {
    public:
         T add(T a, T b)
        {
            return a + b;
        }
    
         T minus(T a, T b)
        {
            return a - b;
        }
    
         T multiply(T a, T b)
        {
            return a * b;
        }
    
         T divide(T a, T b)
        {
            return a / b;
        }
    };
    string operator-(string& l, string& r) //由于string类没有重载减号操作符,所以我们自定义一个 { return "Minus"; } int main() { Operator<int> op1; //定义对象时,需要指定类模板类型 cout << op1.add(1, 2) << endl; Operator<string> op2; //定义对象时,需要指定类模板类型 cout << op2.add("D.T.", "Software") << endl; cout << op2.minus("D.T", "Software") << endl; return 0; }

    运行打印:

    3
    D.T. Software
    Minus

    类模板的工程应用

    • 类模板必须在.h头文件中定义
    • 类模板的成员函数不能分开在不同的文件中实现
    • 类模板外部定义的成员函数,和模板函数一样,还需要加上模板template <typename T>声明,以及结构体<T>声明

    接下来,我们便修改上面代码定义的Operator类模板,只需要写Operator.h文件即可:

    #ifndef  _OPERATOR_H
    #define _OPERATOR_H
    
    template < typename T >
    class Operator
    {
    public:
        T add(T a, T b);
        T minus(T a, T b);
        T multiply(T a, T b);
        T divide(T a, T b);
    };
    
    template < typename T >           //外部定义的成员函数,都需要加上模板声明
    T  Operator<T> :: add(T a, T b)  //同时加上结构体<T>声明
    {
           return a+b;
    }
    
    template < typename T >          
    T  Operator<T> :: minus(T a, T b)
    {
           return a-b;
    }
    template
    < typename T > T Operator<T> :: multiply(T a, T b) { return a*b; } template < typename T > T Operator<T> :: divide(T a, T b) { return a/b; } #endif

    多参数类模板

    类模板可以定义任意多个不同的类型参数,同时还要必须指定每个模板参数

    例如:

    template < typename T1,typename T2 >
    class Operator
    {
    public:
        void add(T1 a, T2 b);
    };
    
    template < typename T1,typename T2 >
    void Operator<T1,T2 > :: add(T1 a, T2 b)
    {
            cout<<a+b<<endl;
    }
    
    int main()
    {
        Operator<int,float> op1;                 //定义op1对象时,必须指定类模板类型
        op1.add(2,2.1);                                  //4.1
        return 0;
    }

    运行打印:

    4.1

    从结果来看,上面的类模板好像已经实现了add加法运算.但是却不能支持指针类型.

    其实,类模板也可以像函数重载一样, 类模板通过特化的方式可以实现特殊情况.

    类模板特化

    • 表示可以存在多个相同的类名,但是模板类型都不一致(和函数重载的参数类似)
    • 特化类型有完全特化部分特化两种类型
    • 完全特化表示显示指定类型参数,模板声明只需写成template<>,并在类名右侧指定参数,比如:
    template < typename T1,typename T2 >  //声明的模板参数个数为2个
    class Operator                        //正常的类模板
    {
    public:
            void add(T1 a, T2 b)
           {
                  cout<<a+b<<endl;
           }
    };
    
    template <>                           //不需要指定模板类型,因为是完全特化的类模板
    class Operator< int , int>           //指定类型参数,必须为2个参数,和正常类模板参数个数一致
    
    {                                               
    
    public:
    
    void add(int a, int b)
    
    {
    
    cout<<a+b<<endl;
    
    }
    
    };
    
    int main()
    
    {
    
           Operator<int,int> Op1;        //匹配完全特化类模板:class Operator< int,int>
    
           Operator<int,float> Op2;     //匹配正常的类模板
    
           return 0;
    
    }
    • 部分特化表示通过特定规则约束类型参数,模板声明和类似,并在类名右侧指定参数,比如:
    template < typename T1,typename T2 >           //声明的模板参数个数为2个
    class Operator                                 //正常的类模板
    {
    public:
            void add(T1 a, T2 b)
           {
                  cout<<a+b<<endl;
           }
    };
    
     
    
    template < typename T >          //有指定模板类型以及指定参数,所以是部分特化的类模板             
    class Operator< T* ,T*>          //指定类型参数,必须为2个参数,和正常类模板参数个数一致
    {
    public:
      void add(T* a, T* b)
      {
                  cout<<*a+*b<<endl;
      }
    };
    
    int main()
    {
         Operator<int*,int*> Op1;            //匹配部分特化: class Operator< T* ,T*>
         Operator<int,float> Op2;           //匹配正常的类模板: class Operator     
    return 0;
    }
    • 编译时,会根据对象定义的类模板类型,首先去匹配完全特化,再来匹配部分特化,最后匹配正常的类模板.

    初探类模板特化

    #include <iostream>
    
    using namespace std; 
    
    template < typename T1,typename T2 >  
    class Operator                                            //正常的类模板
    {
    public:
            void add(T1 a, T2 b)
           {
                  cout<<"add(T1 a, T2 b)"<<endl;
                  cout<<a+b<<endl;
           }
    };
    
    template < typename T >                              
    class Operator<T,T>                                //部分特化的类模板,当两个参数都一样,调用这个
    {
    public:
             void add(T a, T b)
           {
                  cout<<"add(T a, T b)"<<endl;
                  cout<<a+b<<endl;
           }
    };
    
    template < typename T1,typename T2 >  
    class Operator<T1*,T2*>                                   //部分特化的类模板,当两个参数都是指针,调用这个
    {
    public:
            void add(T1* a, T2* b)
           {
                  cout<<"add(T1* a, T2* b)"<<endl;
                  cout<<*a+*b<<endl;
           }
    };
    
    template < >  
    class Operator<void*,void*>                             //完全特化的类模板,当两个参数都是void*,调用这个
    {
    public:
            void add(void* a, void* b)
           {
                  cout<<"add(void* a, void* b)"<<endl;
                  cout<<"add void* Error"<<endl;                 //void*无法进行加法
           }
    };
    
    int main()
    {
           int *p1 = new int(1);
           float *p2 = new float(1.25);
    
           Operator<int,float>  Op1;        //匹配正常的类模板:class Operator      
           Op1.add(1,1.5);
    
           Operator<int,int>  Op2;          //匹配部分特化的类模板:class Operator<T,T>
           Op2.add(1,4);
    
           Operator<int*,float*>  Op3;      //匹配部分特化的类模板:class Operator<T1*,T2*>      
           Op3.add(p1,p2);
    
           Operator<void*,void*>  Op4;      //匹配完全特化的类模板:class Operator<void*,void*>
           Op4.add(NULL,NULL);  
    
           delete p1;
           delete p2;
    
           return 0;
    }

    运行打印:

    add(T1 a, T2 b)
    2.5
    
    add(T a, T b)
    5
    
    add(T1* a, T2* b)
    2.25
    
    add(void* a, void* b)
    add void* Error

    数值型模板参数

    之前,我们学习的模板参数都是带泛型的(表示不同类型),其实模板参数也可以是数值型参数,如下图所示:

     

    • 数值型模板参数必须在编译时被唯一确定

    比如: 变量在运行期间是可变的,所以不能作为模板参数.以及浮点数(不精确),类对象(可变)等等.

     

    接下来,我们便通过数值参数的类模板来求 1+2+3+...+N的值

    代码如下所示:

    template < int N >
    class Sum
    {
    public:
        static const int VALUE = Sum<N-1>::VALUE + N;              //定义静态常量并赋值
    };
    template
    < > class Sum < 1 > { public: static const int VALUE = 1; }; int main() { cout << "1 + 2 + 3 + ... + 10 = " << Sum<10>::VALUE << endl; cout << "1 + 2 + 3 + ... + 100 = " << Sum<100>::VALUE << endl; return 0; }

    运行打印:

    1 + 2 + 3 + ... + 10 = 55
    1 + 2 + 3 + ... + 100 = 5050

     

  • 相关阅读:
    项目部署
    nginx
    IDEA中Lombok插件的安装与使用
    Git常用命令总结
    CentOS 7 NAT模式上网配置
    一名3年工作经验的java程序员应该具备的技能
    maven 项目加载本地JAR
    linux压缩(解压缩)命令详解
    jdk7与jdk8环境共存与切换
    linux服务器卸载本机默认安装的jdk
  • 原文地址:https://www.cnblogs.com/lifexy/p/8781525.html
Copyright © 2011-2022 走看看