1.模板
模板是泛型编程的基础。即编写与类型无关的逻辑代码,也是一种复用的方式。
C++中的模板分为:模板函数、模板类
(1)模板函数
函数模板格式:template<class/typename 形参名1,class/typename 形参名2,class/typename 形参名n>
例子:实现一个比较两个数是否相符
template <typename T>
bool IsEqual(const T& left, const T& right)
{
return left==right;
}
明显的,模板实现了代码的复用,那么模板是如何实现不同类型的参数调用时刚还对号入座呢?
原来,编译器调用模板时,编译器会根据传递的参数自动推演出模板形参的类型,并且自动生成
对应的代码。如下图所示:
需要注意的是:
(1)按照以上例子,如果调用IsEqual(1,1.2)时,两个参数不匹配的情况,我们可以做一个变换
<double>IsEqual(1,1.2),这个过程叫做显示实例化
(2)如果模板自己就定义了两个不相同类型的参数,如下
template <typename T1, typename T2>
bool IsEqual (const T1& left , const T2& right)
{
return left == right;
}
如果调用IsEqual(1,1),编译器先检查,看是否有两个参数都是int型的函数IsEqual()
如果有优先调用它,否则再调用模板函数,这就是模板实例化后的函数与原函数构成重载
(2)模板类
模板类格式:
template<class/typename 形参名1,class/typename 形参名2,...class/typename 形参名n>
class 类名
{...};
例子:用模板类实现动态顺序表
#include <iostream>
#include <assert.h>
#include <string>
using namespace std;
template <typename T>
class SeqList
{
private:
T* _a;
size_t _size;
size_t _capacity;
public:
SeqList()//构造函数
:_a(NULL),_size(0),_capacity(0)
{
//cout<<"SeqList()//构造函数"<<endl;
}
~SeqList()//析构函数
{
if(_a)
{
delete[] _a;
}
}
SeqList(const SeqList<T>& s)//拷贝构造
{
_a=new T[s._size];
for(size_t i=0;i<s._size;i++)
{
_a[i]=s._a[i];
}
_size=s._size;
_capacity=_size;
}
SeqList<T>& operator=(const SeqList<T> s)//赋值运算符的重载
{
swap(s._a, _a);
swap(s._size,_size);
swap(s._capacity,_capacity);
return *this;
}
void CheckCapacity()//判断并且增容
{
if(_size==_capacity)
{
size_t newSize=_capacity?_capacity*2:3;//刚进来增到3,下次每次增2倍容
T* tmp=new T[newSize];
for(size_t i=0; i<_size;i++)
{
tmp[i]=_a[i];//执行赋值运算符的重载
}
delete[] _a;
_a=tmp;
_capacity=newSize;
assert(_a);
}
}
void PushBack(const T& x)//后插
{
CheckCapacity();
_a[_size++]=x;
}
void PopBack()//后删
{
_size--;
}
void Insert(size_t pos, const T& x)//在某个位置前插入一个元素
{
CheckCapacity();
assert(pos>=0);
for(size_t i=_size;i>pos;i--)
{
_a[i]=_a[i-1];
}
_a[pos]=x;
_size++;
}
void Erase(size_t pos)//删除
{
assert(pos>=0);
for(size_t i=pos;i<_size-1;i++)
{
_a[i]=_a[i+1];
}
_size--;
}
T& operator[](size_t pos)//[]重载
{
assert((pos>=0)&&(pos<_size));
return _a[pos];
}
void Print()//打印元素
{
//int i=0;
for(size_t i=0;i<_size;i++)
{
cout<<_a[i]<<" ";
}
cout<<endl;
}
};
需要注意的是:
(1)这时候,模板类的类型不是SeqList,而是SeqList<T>.
(2)需要开辟空间的时候用new,而不能用realloc,因为用new回调用函数初始化空间,导致每次释放没问题
而realloc第一次进来相当于malloc,空间内容没有初始化,导致释放随机值有问题。
(3)和模板函数类似,在调用模板类的时候,编译器也会根据类型SeqList<T>来模板推演处理,自己
换取相对应类型的模板,如下图所示: