今天,我们来学习总结一下有关C++中模板的概念和知识。
一.模板的概念
1.我们知道,函数的调用要根据给出的实参类型以及函数名来确定,拿通用的加法函数来说,不同的实参所调用的函数不同。因此我们需要通过函数重载来实现对不同类型数据的加法操作。
int Add(const int & left, const int & right)
{
return left + right;
}
double Add(const double &left, const double&right)
{
return left + right;
}
这个过程只要有新类型的出现,我们就需要添加新函数。代码效率不高,而且复用率差。而且函数重载如果仅仅是返回值不同是无法解决的。
如果使用公共基类来编写通用的代码,将失去类型检查的优点。以后要实现的类,都必须公共的继承自某一个基类,使得代码的维护更加困难。
如果采用宏处理来实现,首先 ,宏并不是一个函数,它不会进行类型检测,因此安全系数差。
2.下面我们就来介绍泛型编程:泛型编程就是编写与类型无关的逻辑代码,这是实现代码复用的一种高效的手段。而模板就是泛型编程的基础。
模板分为两大类:(1)函数模板;(2).类模板
(1)函数模板:它代表了一个函数家族,函数与类型无关,在使用时被参数化,根据实参类型产生特定的函数版本。
模板函数的定义:关键字(template)
template<class T>
T Add(T left, T right)
{
return left + rght;
}
typename是用来定义模板参数的关键字,也可以使用class,注意struct不能代替两者。
模板函数也可以声明为内联函数,但是关键字inline必须放在模板参数列表之后,返回值之前,不能放在template之前
1.1模板实例化
模板不是类或者函数,在被编译器产生指定类型的类或者函数的过程我们称之为模板实例化。
template<class T>
T Add(T left, T right)
{
return left + rght;
}
int main()
{
cout << Add(10, 20);
cout << Add(1.2, 3.2);
cout << Add('1', '2');
return 0;
}
在编译器编 译调用函数过程中,编译器会根据实参来确定模板的模板参数类型,从而调用模板函数。比如上面的代码,当我们第一次调用两个整形作为实参时,相应的模板函数也会就会把模板函数实例化两个整形的加法函数。
下面是程序的编译调用过程:
1.实例化之前,编译器先检查模板代码本身,查看是否有语法错误。
2.实例化期间,检查模板代码,查看是否所有的调用都有效。注意:实例化类型不支持某些函数的调用
3.实参推演,从函数实参确定模板形参类型,多个类型形参的实参必须完全匹配
4.类型形参转换:const转换:接收const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用;
数组或者函数到指针的转换:如果模板形参不是引用类型,则对数组或者函数类型的实参应用常规指针转换,数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
模板参数
模板参数有两种,第一种是模板参数和调用参数。
模板形参又分为类型参数和非类型参数。
类型参数即类型不确定,而非类型参数即参数为确定的内置类型。
注意:模板的形参名字只能在模板形参之后到模板的声明或者定义的末尾之间使用,而且遵循名字的屏蔽规则。
模板的形参名字在同一模板形参列表中只能使用一次;
所有模板形参前面必须加上class或者typename;
在函数模板的内部不能指定缺省的模板实参。
#include<iostream>
using namespace std;
typedef int T;
template<class T>
void FunTest(T t)
{
cout << "t Type=" << typeid(T).name() << endl;
return left+right;
}
T gloab;
int main()
{
FunTest(10);
cout << "gloab Type" << typeid(gloab).name() << endl;
getchar();
return 0;
}
函数模板的特化
template<class T>
返回值 函数名<Type>(参数列表)
{
//函数体
}
注意:特化的声明必须与特定的模板相匹配,而且const T p在编译时实际上是T const p
二.模板类
类模板也是模板,必须以template关键字开头,后接模板形参表。
templat<class形参名1,class 形参名2,class形参名3>
注意:在类外定义的类的成员函数前面也要加上模板参数列表,并且要加上类的作用域。
浮点数和类对象是不允许作为非类型模板参数的。
模板的实例化:全特化与偏特化
1.全特化
template<typename T>
class SeqList
{
public:
SeqList()
~SeqList();
private:
int _size;
int _cacpacity;
T*_data;
};
template<typename T>
SeqList<T>::SeqList()
:_size(0)
, _cacpacity(0)
, _data(new T[_cacpacity])
{
}
2.偏特化
template<class T>
class Data(T1, int)
{
public:
Data();
private:
T1 _d1;
int _d2;
}
模板的分离编译
解决方法:
1.在模板头文件xxx.h里面显示实例化->模板类的定义后面添加template class SeqList<int>;
2.将声明和定义放到一个文件xxx.hpp里面。