美文网首页
C++中的类模板

C++中的类模板

作者: nethanhan | 来源:发表于2017-10-14 11:01 被阅读0次

    类模块的概念和意义

    在C++中有这样一些类:

    • 主要用于存储和组织数据元素
    • 类中数据组织的方式和数据元素的具体类型无关
    • 如:数组类,链表类,Stack类,Queue类等

    C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。

    所以C++中的类模板是这样的:

    • 以相同的方式处理不同类型的数据
    • 在类声明前使用template进行标识
    • < typename T >用于说明类中使用的泛指类型 T
    • 语法:
    template<typename T>
    class Operator
    {
    public:
        T op(T a, T b);
    };
    

    类模板的应用:

    • 只能显示指定具体类型,无法自动推导
    • 使用具体类型 < Type > 定义对象
    • 用法:
    Operator <int> op1;
    Operator <string> op2;
    int i = op1.op(1, 2);
    string s = op2.op("D.T.", "Software");
    

    类模板的进一步理解:

    • 声明的泛指类型 T 可以出现在类模板的任意地方
    • 编译器对类模板的处理方式和函数模板相同
      • 从类模板通过具体类型产生不同的类
      • 在声明的地方对类模板代码本身进行编译
      • 在使用的地方对参数替换后的代码进行编译

    这里举一个例子:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    //重载string类减号类型操作符
    string operator-(string& l, string& r)
    {
        return "Minus";
    }
    
    //定义一个类模板
    //在类模板中有4个操作
    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;
        }
    };
    
    int main()
    {
        //使用类模板创建一个对象,类型为int
        Operator<int> op1;
        //对象调用类的成员函数
        cout << op1.add(1, 2) << endl;
        
        //使用类模块创建一个对象,类型为string
        Operator<string> op2;
        //对象调用类的成员函数
        cout << op2.add("D.T.", "Software") << endl;
        cout << op2.minus("D.T", "Software") << endl;
        
        return 0;
    }
    

    输出结果为:

    3
    D.T.Software
    Minus
    

    类模板在工程中是怎么使用的呢?

    • 类模块必须在头文件中定义
    • 类模块不能分开实现在不同的文件中
    • 类模块外部定义的成员函数需要加上模板 < > 声明

    这里做一个示例:

    在头文件 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)
    {
        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
    

    调用使用时:

    #include <iostream>
    #include <string>
    #include "Operator.h"
    
    using namespace std;
    
    int main()
    {
        //使用类模块创建类对象
        Operator<int> op1;
        
        //类对象使用各个成员函数
        cout << op1.add(1, 2) << endl;
        cout << op1.multiply(4, 5) << endl;
        cout << op1.minus(5, 6) << endl;
        cout << op1.divide(10, 5) << endl;
        
        return 0;
    }
    

    输出结果为:

    3
    20
    -1
    2
    

    多参数类模块

    在类模块中可以定义任意多个不同的类型参数,比如这样:

    template 
    <typename T1, typename T2>
    class Test
    {
    public:
        void add(T1 a, T2 b);
    };
    
    //使用
    Test<int, float> t;
    

    类模块的特化

    这里再学习一个类模块的知识,就是它可以被 特化

    • 指定类模块的特定实现
    • 部分类型参数必须显示指定
    • 根据类型参数分开实现类模块
    • 语法:
    template
    <typename T1, typename T2>
    class Test
    {
    };
    
    //特化
    template
    <typename T>
    class Test <T, T>
    {
    };
    

    类模块可以被 特化 ,当然对于特化还有特化类型:

    • 部分特化 --- 用特定规则约束类型参数
    • 完全特化 --- 完全显示指定类型参数
    template
    <typename T1, typename T2>
    class Test
    {
    };
    
    //完全特化
    template
    <  >
    class Test < int, int >
    {
    };
    

    这里举一个例子:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template
    < typename T1, typename T2 >
    //正常的类模块
    class Test
    {
    public:
        void add(T1 a, T2 b)
        {
            cout << "void add(T1 a, T2 b)" << endl;
            cout << a + b << endl;
        }
    };
    
    template
    < typename T1, typename T2 >
    // 关于指针的特化实现
    //部分特化:用指针类型约束类型参数
    class Test < T1*, T2* >
    {
    public:
        void add(T1* a, T2* b)
        {
            cout << "void add(T1* a, T2* b)" << endl;
            cout << *a + *b << endl;
        }
    };
    
    template
    < typename T >
    // 当 Test 类模板的两个类型参数完全相同时,使用这个实现
    //部分特化:用参数类型完全相等的规则约束
    class Test < T, T >
    {
    public:
        void add(T a, T b)
        {
            cout << "void add(T a, T b)" << endl;
            cout << a + b << endl;
        }
        void print()
        {
            cout << "class Test < T, T >" << endl;
        }
    };
    
    template
    <  >
    // 当 T1 == void* 并且 T2 == void* 时
    //完全特化 完全显示指定类型参数
    class Test < void*, void* >
    {
    public:
        void add(void* a, void* b)
        {
            cout << "void add(void* a, void* b)" << endl;
            cout << "Error to add void* param..." << endl;
        }
    };
    
    int main()
    {  
        //2个类型不同,调用普通类模块
        Test<int, float> t1;
        //2个类型相同,调用用参数类型完全相等的规则约束的类模块
        Test<long, long> t2;
        //2个类型完全相等,并且符合已经指定参数类型的类模板
        Test<void*, void*> t3;
        
        t1.add(1, 2.5);
        
        t2.add(5, 5);
        t2.print();
        
        t3.add(NULL, NULL);
        //2个参数都为指针,且类型不同
        //调用用指针类型约束类型参数的类模板
        Test<int*, double*> t4;
        int a = 1;
        double b = 0.1;
        
        t4.add(&a, &b);
        
        return 0;
    }
    

    输出结果为:

    void add(T1 a, T2 b)
    3.5
    void add(T a, T b)
    10
    class Test < T, T >
    void add(void* a, void* b)
    Error to add void* param...
    void add(T1* a, T2* b)
    1.1
    

    使用类模块特化也要注意一些地方:

    • 特化只是模块的分开实现
      • 本质上是同一个类模块
    • 特化类模板的使用方式是统一的
      • 必须显示指定每一个类型参数

    类模块特化的进一步理解

    其实有没有发现特化和重定义有点相似,但也有些区别:

    • 重定义
      • 一个类模块和一个新类(或者两个类模块)
      • 使用的时候需要考虑如何选择的问题
    • 特化
      • 以统一的方式使用类模块和特化类
      • 编译器自动优先选择特化类

    那既然类模块可以特化,函数模块可不可以特化呢?

    • 函数模板只支持类型参数完全特化
    • 使用方法:
    template
    <typename T>
    //函数模块定义
    bool Equal(T a, T b)
    {
        return a == b;
    }
    
    template
    < >
    //函数模块完全特化
    bool Equal<void *>(void* a, void* b)
    {
        return a == b;
    }
    

    这里举一个例子:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    //普通函数模块
    template
    < typename T >
    bool Equal(T a, T b)
    {
        cout << "bool Equal(T a, T b)" << endl;
        
        return a == b;
    }
    
    //完全特化后的函数模块
    template
    < >
    bool Equal<double>(double a, double b)
    {
        const double delta = 0.00000000000001;
        double r = a - b;
        
        cout << "bool Equal<double>(double a, double b)" << endl;
        
        return (-delta < r) && (r < delta);
    }
    
    //普通函数
    bool Equal(double a, double b)
    {
        const double delta = 0.00000000000001;
        double r = a - b;
        
        cout << "bool Equal(double a, double b)" << endl;
        
        return (-delta < r) && (r < delta);
    }
    
    int main()
    {  
        //调用函数
        cout << Equal( 1, 1 ) << endl;
        //调用完全特化后的函数模块
        cout << Equal<>( 0.001, 0.001 ) << endl;
        
        return 0;
    }
    

    输出结果为:

    bool Equal(T a, T b)
    1
    bool Equal<double>(double a, double b)
    1
    

    这里要注意:

    当需要重载函数模块时,优先考虑使用模板特化;当模板特化无法满足需求,再使用函数重载!
    

    小结

    • 泛型编程的思想应用于类的形式就是类模块
    • 类模板以相同的方式处理不同类型的数据
    • 类模块非常适用于编写数据结构相关的代码
    • 类模块在使用时只能显示指定类型
    • 类模块可以定义任意多个不同的类型参数
    • 类模块可以被部分特化和完全特化
    • 特化的本质是模板的分开实现
    • 函数模板只支持完全特化
    • 工程中使用模板特化代替类(函数)重定义

    相关文章

      网友评论

          本文标题:C++中的类模板

          本文链接:https://www.haomeiwen.com/subject/wbsxuxtx.html