不一样的C++系列--类模板与特化

来源:互联网 发布:网络爬虫技术介绍 编辑:程序博客网 时间:2024/05/21 13:57

类模板

类模块的概念和意义

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

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

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

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

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

类模板的应用:

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

类模板的进一步理解:

  • 声明的泛指类型 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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

输出结果为:

3D.T.SoftwareMinus
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

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

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

这里做一个示例:

在头文件 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

调用使用时:

#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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出结果为:

320-12
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

多参数类模块

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

template <typename T1, typename T2>class Test{public:    void add(T1 a, T2 b);};//使用Test<int, float> t;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

类模块的特化

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

  • 指定类模块的特定实现
  • 部分类型参数必须显示指定
  • 根据类型参数分开实现类模块
  • 语法:
template<typename T1, typename T2>class Test{};//特化template<typename T>class Test <T, T>{};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

  • 部分特化 — 用特定规则约束类型参数
  • 完全特化 — 完全显示指定类型参数
template<typename T1, typename T2>class Test{};//完全特化template<  >class Test < int, int >{};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里举一个例子:

#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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

输出结果为:

void add(T1 a, T2 b)3.5void add(T a, T b)10class Test < T, T >void add(void* a, void* b)Error to add void* param...void add(T1* a, T2* b)1.1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

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

类模块特化的进一步理解

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

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

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

  • 函数模板只支持类型参数完全特化
  • 使用方法:
template<typename T>//函数模块定义bool Equal(T a, T b){    return a == b;}template< >//函数模块完全特化bool Equal<void *>(void* a, void* b){    return a == b;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里举一个例子:

#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;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

输出结果为:

bool Equal(T a, T b)1bool Equal<double>(double a, double b)1
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

这里要注意:

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

小结

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