什么是泛型编程?
–不考虑具体数据类型的编程方式
泛型编程的意义:
–泛型编程就是为了代码复用! 是C++中重要的代码复用方式
建议:阅读本文时先总看框架,再分看细节,这样效果比较好。
例: 对于Swap函数可以考虑下面的泛型写法
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
此泛型写法中的T不是具体的数据类型,而是泛指任意的数据类型。
--是一种特殊的函数可以用不同类型进行调用
--看起来和普通函数很相似,区别是类型可被参数化
函数模板的语法规则
-template 关键字用于声明开始进行泛型编程
-typename 关键字用于声明泛指类型
例:
template
void Swap(T& a, T& b)
{
T t = a;
a = b;
b = t;
}
函数模板的使用
--自动类型推导调用
--具体类型显示调用
例:
int a = 0;
int b = 1;
Swap(a, b); //自动推导
float c = 2;
float d = 3;
Swap(c, d); //显示调用
函数模板深入理解
-编译器从函数模板通过具体类型产生不同的函数,然后对函数模板进行两次编译
1,对模板代码本身进行编译
2,对参数替换后的代码进行编译
注意事项:
-函数模板本身不允许隐式类型转换
自动推导类型时必须严格匹配
显示类型指定时,能够进行隐式类型转换 !!!建议显示调用
函数模板可以定义任意多个不同的类型参数
对于多参数函数模板,无法自动推导返回值类型,可以从左向右部分指定类型参数
template < typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
return static_cast(a + b);
}
int r1 = Add(0.5, 0.8); //T1 = int, T2 = double, T3 = double
int r2 = Add(0.5, 0.8); //T1 = int, T2 = float, T3 = double
int r3 = Add(0.5, 0.8); // T1 = int, T2 = float, T3 = float
注: 工程中将返回值作为第一个类型参数!! 返回值类型必须显示指定。
函数重载与函数模板
函数模板可以像普通函数一样被重载
-C++编译器优先考虑普通函数
-如果函数模板可以产生一个更好的匹配,那么选择模板
-可以通过空模板实参列表限定编译器只匹配模板
例:
int r1 = Max(1, 2);
double r2 = Max<>(0.5, 0.8);
限定编译器只匹配函数模板
一些类主要用于存储和组织数据元素,类中数据组织的方式和数据元素的具体类型无关
如:数组类、链表类、Stack类、Queue类,等
C++中将模板的思想应用于类,使得类的实现不关注数据元素的具体类型,而只关注类所需要实现的功能。
C++中的类模板
-以相同的方式处理不同的类型
-在类声明前使用template进行标识
-用于说明类中使用的泛指类型T
例:
template < typename T >
class Operator
{
public:
T op(T a, T b);
};
类模板的应用
-只能显示指定具体类型,无法自动推导
-使用具体类型定义对象
例:
Operator op1;
Operator op2;
int i = op1.op(1, 2);
String s = op2.op("yang", "wen");
注意:
声明的泛指类型 T 可以出现在类模板的任意地方
编译器对类模板的处理方式和函数相同
-从类模板通过具体类型产生不同的类
-在声明的地方对类模板代码本身进行编译
-在使用的地方对参数替换后的代码进行编译
类模板的工程应用
-类模板必须在头文件中定义
-类模板不能分开实现在不同的文件中
-类模板外部定义的成员函数需要加上模板<>声明
类模板可以定义任意多个不同的类型参数
例:
template
class Test
{
public:
void add(T1 a, T2 b);
};
Test t;
类模板可以被特化
特化:指定类模板的特定实现,部分类型参数必须显示指定,根据类型参数分开实现类模板
例:
template
class Test
{
};
特化后:
template
class Test< T, T > //规定的地方在这里
{
};
注意观察: 特化前,参数T可以在任意地方,特化后,参数出现在了规定的地方。
类模板的特化类型
特化类型分两种:1.部分特化:用特定规则约束类型参数,见上例。
2.完全特化: 完全显示指定类型参数
例:
template
class Test
{
};
完全特化后:
template < >
class Test< int, int > //具体指定类型参数
{
};
注意观察:特化前,T参数可以出现在类中任意地方,部分特化后,出现在了规定的地方,完全特化后,出现了具体的类型参数。
类模板特化注意事项
特化只是模板的分开实现,本质上是同一类模板;
特化类模板的使用方式是统一的,就是必须显示指定每一个类型参数
重定义与特化的比较
重定义:一个类模板和一个新类(或者两个类模板),使用的时候需要考虑如何选择
特化:以统一的方式使用类模板和特化类,编译器自动优先选择特化类
函数模板只能支持类型参数完全特化,而不支持部分特化
例:
template < typename T > //函数模板定义
bool Equal(T a, T b)
{
return a == b;
}
完全特化后:
template < >
bool Equal(void* a, void* b)
{
return a == b;
}
注意:在工程中,当需要重载函数模板时,优先考虑使用模板特化;
当模板特化无法满足需求,再使用函数重载!
白话理解:
在编程写代码的时候,函数模板与类模板实际上与普通的函数和类没有什么区别,关键的区别在于:
把具体的数据类型换成了未知的T类型,因此叫模板。 编译器在编译的时候先编译模板,实际上就是检查模板,
然后根据使用的地方的参数来具体替换T,然后进行二次编译,二次编译与普通的编译是一样的。
灵活的地方:就是我们可以在使用的地方任意指定参数。也是代码复用的地方。
C++中的泛型编程包括函数模板和类模板,在学习的时候,首先我们应该知道这两个存在的目的是: 代码复用! 然后要知道特性是接受不同类型的参数,因此可以想到其应用的地方在哪里,最后应该知道怎么样去使用,以及使用时应该注意的细节问题,咋一看比较复杂,实际上静下心来好好理解,反复理解,慢慢的就明白了。
注:本文由作者在狄泰软件学院学习、总结、思考所得,欢迎交流。