1 模板概念
模板并不是真正的函数或类,只是一个模具,当有实例化的时候,就会生成相应的函数或类。
模板的种类:
2 函数模板
2.1 函数模板概念
该模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
2.2 函数模板格式
template<typename T1,typename T2,typename T3,typename T4,.......>
,typename是用来定义模板类型的关键字template<class T1,class T2,class T3,class T4,.......>
,这里不能用struct代替class
例:
template <class T>
T ADD(T left, T right)//模板函数
{
return left + right;
}
2.3 函数模板的调用原理
在编译阶段,编译器对传入的实参类型进行推演,根据其推演的实参类型,生成具体类型的函数以供调用。
例:
template <class T>
T ADD(T left, T right)//模板函数
{
return left + right;
}
int main()
{
ADD(1, 2);//int类型
ADD(1.0, 2.0);//double类型
ADD('A', 'B');//char类型
return 0;
}
其反汇编中调用的函数为:不同的类型调用了不同的函数。
2.4 函数模板的实例化
例:
template <class T>
T ADD(T left, T right)//模板函数
{
return left + right;
}
int main()
{
ADD(1, 2);
ADD(1.0, 2.0);
ADD('A', 'B');
ADD(1, 1.1);//该类型不能通过编译
//ADD(1,(int)1.1);//强转类型
return 0;
}
第四种为什么编译不会通过呢?
在模板中,编译器一般不会进行隐式类型转换。若没有类型匹配的函数,则直接进行报错。
解决方法:①用户自己强转,②使用多个类模板参数
template <class T1, class T2>//
T2 ADD(T1 left, T2 right)//多模板参数函数
{
return left + right;
}
- 显式实例化:在调用函数名后的
<>
中指定模板参数的实际类型
例:
template <class T>
T ADD(T left, T right)//模板函数
{
return left + right;
}
int main()
{
ADD(1, 2);
ADD(1.0, 2.0);
ADD('A', 'B');
ADD<int>(1, 1.1);//显式实例化
return 0;
}
编译器就会根据用户所提供的函数模板生成函数。若类型不匹配,则会隐式类型转换。转换成功则执行代码,转换失败则会报错。
2.5 函数模板调用规则
- 非模板函数和同名模板函数同时存在,并且该模板函数可以被实例化为该非模板函数。在调用时,若是隐式实例化为该非模板函数,则调用非模板函数,显式实例化为该模板函数,则调用模板函数。
- 若是模板函数产生了一个具有更换匹配的函数,则调用模板函数。
3 类模板
只要用户对类模板进行了不同类型的实例化,编译器在编译阶段就会生成不同的类。
例:
template <class T>
class SeqList
{
private:
T* _data;
size_t _size;
size_t capacity;
};
3.1 类模板实现一个顺序表
template <class T>
class SeqList
{
public:
SeqList(size_t capacity = 15)//构造函数
:_data(new T[capacity])
,_size(0)
,_capacity(capacity)
{}
SeqList(SeqList<T>& psq)//拷贝构造函数
{
if (psq._data != nullptr)
{
_data = new T[psq._capacity];
_capacity = psq._capacity;
_size = psq._size;
memcpy(_data, psq._data, sizeof(T) * psq._size);
}
}
~SeqList()//析构函数
{
if (_data)
delete[] _data;
_size = _capacity = 0;
}
void SeqListPushBack(T data)//尾部插入
{
if (_size == _capacity)
{
cout << "顺序表已满,不能插入" << endl;
return;
}
_data[_size++] = data;
}
void SeqListPopBack()//尾部删除
{
if (_size == 0)
{
cout << "顺序表为空,不能删除" << endl;
return;
}
_size--;
}
T SeqListSize()//查看元素个数
{
return _size;
}
T& operator[](int index)
{
assert(index >= 0 && index < _capacity);
return _data[index];
}
private:
T* _data;
size_t _size;
size_t _capacity;
};