什么叫泛型编程?
泛型编程是一种新的编程思想,基于模板技术有效的将算法和数据结构分离,编写不依赖具体数据类型的程序,将程序尽可能通用,将算法从数据结构中抽象出来,成为通用算法。C++中模板为泛型程序设计提供了基础,STL(标准模板库Standard Template Library)是泛型程序设计的例子。
模板概念
模板是一种用不确定类型参数来产生一系列函数和类的机制,通过模板我们可以产生类和函数的集合,使他们操作不同的数据类型,从而避免为每一种数据类型单独产生一个函数和类,模板分为函数模板和类模板。
模板只是说明,需要实例化之后才能执行和使用
函数模板
函数模板使用和普通函数一样,但它的类型可以通过参数来传递,在函数定义时可以不指明具体的数据类型,当发送函数调用时,编译器可根据传入的实参自动推断数据类型。
语法:
template <<模板参数表>>
<返回值类型><函数名>(<参数表>){
<函数体>
}
template:函数模板定义关键字
<模板参数表>:用来定义函数参数的类型,参数类型有一个或多个,用逗号隔开,不能为空,模板参数常用形式 typename class
为何要用到函数模板?
举例:交换两个数的方法
宏定义:
优点:代码复用,适合所有的类型
缺点:缺少类型检查,宏在预处理阶段被代替掉,编译器不知道宏的存在
函数:
优点:真正的函数调用,编译器对类型进行检查
缺点:类型不同时需要重复定义函数,代码无法复用
函数模板:
优点:代码复用,适合所有类型。克服普通函数弊端,编译器会进行类型检查。克服宏定义弊端
缺点:调试比较难,对程序员要求高,—般编写一个类型确定的函数,运行通过后,再修改成函数模板
调用方式:1.自动类型推导调用 swap(a, b); 1.具体类型显示调用(推荐使用)Swap<int>(a, b);
注意:函数模板不允许隐式类型转换,调用时类型必须严格匹配
原理分析: 函数模板中声明了参数类型T,表示了一种抽象类型,编译器检查到程序调用函数的时候,根据传递参数的实际类型生成模板函数。
类模板
定义:
是对一批仅仅成员数据类型(成员函数和程序数据)不同的类的抽象,程序员只要为这一批类所组成的整个类家族创建一个类模板,给出一套程序代码,就可以用来生成多种具体的类。
类模板定义格式:
template <<模板参数表>>class <类模板名>
{
<成员函数与成员变量>
};
成员函数在类外定义格式:
template <<模板参数表>>
<返回值类型><类模板名><<模板参数名列表>>:;<成员函数名>(<参数表>){
<函数体>
}
类模板定义对象:
类模板名<真实类型参数表>对象名(构造函数实际参数表)
原理:编译器由类模板生成类的过程叫做类模板的实例化,编译器自动用具体的数据类型替换类模板中的类型参数,生成模板类的代码,由类模板实例化得到的类叫做模板类。
类模板与非类型参数
template <class T, int elementsNumber> elementsNumber 不能是浮点类型,不能是类类型!
示例:顺序表
类定义:
#include<iostream>
#include<cstring>
using namespace std;
template <typename T,int size>
class SeqList
{
public:
SeqList();
~SeqList();
bool addData(T value);
bool delData();
void show();
void clear();
private:
T *data;
int index;
};
构造函数与析构函数实现:
template <typename T,int size>
SeqList<T,size>::SeqList()
{
data = new T[size];
index = 0;
}
template <typename T,int size>
SeqList<T,size>::~SeqList()
{
delete []data;
}
添加数据函数和删减数据函数实现:
template <typename T,int size>
bool SeqList<T,size>::addData(T value)
{
if(index >= size)
{
cout << "顺序表已满" << endl;
return false;
}
data[index] = value;
index++;
return true;
}
template <typename T,int size>
bool SeqList<T,size>::delData()
{
if(index > 0)
{
--index;
return true;
}
return false;
}
遍历顺序表实现:
template <typename T,int size>
void SeqList<T,size>::show()
{
for(int i = 0; i< index; ++i)
{
cout << data[i] << " ";
}
cout << endl;
}
清空函数实现:
template <typename T,int size>
void SeqList<T,size>::clear()
{
index = 0;
}
主函数调用:
int main(int argc, const char *argv[])
{
SeqList <int,5>A;
A.addData(1);
A.addData(2);
A.addData(3);
A.addData(4);
A.addData(4);
A.addData(4);
A.addData(4);
A.delData();
A.delData();
A.show();
return 0;
二次编译机制:
在编译器进行编译的时候,编译器会产生类的模板函数的声明,当时实际确认类型后调用的时候,会根据调用的类型进行再次帮我们生成对应类型的函数声明和定义。
二次编译机制在模板类中声明友元函数,且函数实现在类外时会因为编译器找不到友元函数的实现而报错。