类模块的概念和意义
在C++中有这样一些类:
- 主要用于存储和组织数据元素
- 类中数据组织的方式和数据元素的具体类型无关
- 如:数组类,链表类,Stack类,Queue类等
C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。
所以C++中的类模板是这样的:
- 以相同的方式处理不同类型的数据
- 在类声明前使用template进行标识
- < typename T >用于说明类中使用的泛指类型 T
- 语法:
template
class Operator
{
public:
T op(T a, T b);
};
类模板的应用:
- 只能显示指定具体类型,无法自动推导
- 使用具体类型 < Type > 定义对象
- 用法:
Operator op1;
Operator op2;
int i = op1.op(1, 2);
string s = op2.op("D.T.", "Software");
类模板的进一步理解:
- 声明的泛指类型 T 可以出现在类模板的任意地方
- 编译器对类模板的处理方式和函数模板相同
- 从类模板通过具体类型产生不同的类
- 在声明的地方对类模板代码本身进行编译
- 在使用的地方对参数替换后的代码进行编译
这里举一个例子:
#include
#include
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 op1;
//对象调用类的成员函数
cout << op1.add(1, 2) << endl;
//使用类模块创建一个对象,类型为string
Operator 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::add(T a, T b)
{
return a + b;
}
//实现类模块中各个成员函数的逻辑
template < typename T >
T Operator::minus(T a, T b)
{
return a - b;
}
//实现类模块中各个成员函数的逻辑
template < typename T >
T Operator::multiply(T a, T b)
{
return a * b;
}
//实现类模块中各个成员函数的逻辑
template < typename T >
T Operator::divide(T a, T b)
{
return a / b;
}
#endif
调用使用时:
#include
#include
#include "Operator.h"
using namespace std;
int main()
{
//使用类模块创建类对象
Operator 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
class Test
{
public:
void add(T1 a, T2 b);
};
//使用
Test t;
类模块的特化
这里再学习一个类模块的知识,就是它可以被 特化
:
- 指定类模块的特定实现
- 部分类型参数必须显示指定
- 根据类型参数分开实现类模块
- 语法:
template
class Test
{
};
//特化
template
class Test
{
};
类模块可以被 特化
,当然对于特化还有特化类型:
- 部分特化 --- 用特定规则约束类型参数
- 完全特化 --- 完全显示指定类型参数
template
class Test
{
};
//完全特化
template
< >
class Test < int, int >
{
};
这里举一个例子:
#include
#include
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 t1;
//2个类型相同,调用用参数类型完全相等的规则约束的类模块
Test t2;
//2个类型完全相等,并且符合已经指定参数类型的类模板
Test t3;
t1.add(1, 2.5);
t2.add(5, 5);
t2.print();
t3.add(NULL, NULL);
//2个参数都为指针,且类型不同
//调用用指针类型约束类型参数的类模板
Test 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
//函数模块定义
bool Equal(T a, T b)
{
return a == b;
}
template
< >
//函数模块完全特化
bool Equal(void* a, void* b)
{
return a == b;
}
这里举一个例子:
#include
#include
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 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);
}
//普通函数
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 a, double b)
1
这里要注意:
当需要重载函数模块时,优先考虑使用模板特化;当模板特化无法满足需求,再使用函数重载!
小结
- 泛型编程的思想应用于类的形式就是类模块
- 类模板以相同的方式处理不同类型的数据
- 类模块非常适用于编写数据结构相关的代码
- 类模块在使用时只能显示指定类型
- 类模块可以定义任意多个不同的类型参数
- 类模块可以被部分特化和完全特化
- 特化的本质是模板的分开实现
- 函数模板只支持完全特化
- 工程中使用模板特化代替类(函数)重定义