目录
1. 什么是模板?
2. 函数模板
2.1 函数模板的概念
2.2 函数模板的格式
2.3 函数模板的原理
2.4 函数模板的实例化
2.5 模板参数的匹配原理
3. 类模板
3.1 类模板的定义格式
3.2 类模板的实例化
4. 非类型参数模板
5. 模板的特化
5.1 特化的概念
5.2 函数特化
5.3 类模板的特化
5.3.1 全特化
5.3.2 偏特化
6. 模板的优缺点
如果我们想实现对不同类型的数据进行交换,首先我们想到的是进行重载,但是重载有几个不好的地方:
那么有没有比重载更好的方法呢?,我们可不可以告诉编译器一个模板,让它根据不同类型生成不同函数呢?
这就是模板的特点:编写与类型无关的代码,充分利用了代码的复用性
函数模板代表了一个函数集体,该模板与类型无关,根据传入的参数类型生成不同的代码
template
返回值类型 函数名(参数列表)
{
//函数体
}
例如:
这样就实现了简单的函数模板
什么是函数模板?
实质上函数模板只是一个蓝图,它本身不是函数,是编译器帮助我们将该蓝图生成了一个又一个的代码,将我们要做的事情,交给了编译器去做。
用不同类型的参数使用模板,称为函数模板的实例化,模板参数的实例化又分为:显示实例化和隐式实例化。
1. 隐式实例化
如果我们传入了不同类型的参数,编译器会根据传入的类型推出T的类型,这就是隐式实例化。
但隐式实例化也有些问题,如果传入了两个不同的类型,编译器无法推出T的类型是哪一个,又怎么办呢?
这时有两个办法:
2.显示实例化
2.显示实例化
在函数后面加上<>,里面写上传入的数据类型,编译器会生成相应类型的代码。
2. 对于非模板函数和模板函数,如果其他条件相同,则会优先调用非模板函数,而不会调用 模板函数
为什么会优先调用非模板函数呢?,这是因为会优先调用适合的,并且是已存在的,模板函数还需要进行实例化才能调用
那既然函数存在通用的问题,那么类模板也存在着上述问题,如果仅仅只是成员的类型不同就需要重新写一份类,那也太麻烦了,更加违背了代码的复用性,因此C++也提供了对类进行模板的设计。
template
class 模板名
{
//类成员定义
};
举例:
// 动态顺序表
// 注意:Vector不是具体的类,是编译器根据被实例化的类型生成具体类的模具
template
class Vector
{
public :
Vector(size_t capacity = 10)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
// 使用析构函数演示:在类中声明,在类外定义。
~Vector();
void PushBack(const T& data);
void PopBack();
// ...
size_t Size() {return _size;}
T& operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
//类模板中成员函数放在类外进行定义时,需要加模板参数列表
template
Vector::~Vector()
{
if(_pData)
delete[] _pData;
_size = _capacity = 0;
}
在类内声明,类外定义时,必须要添加模板参数列表,让编译器识别此处是一个模板,并且在实现函数时,必须要添加上作用域
类模板的实例化必须在类名后加上<>,在<>里面需要添加实例化的类型,类模板名不是真正的类,加上了传入的类型组合起来才是真正的类。
类模板的实例化和函数模板的实例化不同,函数模板的实例化可以隐式实例化,而类模板只能显示实例化,否则编译器将无法对该模板进行操作。
例如上面的类模板:
vector只是类名
只有类名后面加上了
才代表类型 比如:
vector
、vector
参数模板分为:类型参数模板和非类型参数模板
类型参数:跟在class 和 typenam后面的d名称
非类型参数:用一个变量(常量)来作为模板的参数
非类型参数模板比较典型的就是array:
我们可以发现array的参数列表 中不仅仅是class,还存在size_t类型
非类型参数模板特点:
什么是特化?
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型可能会造成一些问题,对于这种类型需要特殊处理
比如:
我们发现当传入地址时,得到的结果并不是我们想要的结果。
我们可以发现当传入的是类型时,结果是正确的,可是当我们传入指针时,结果却和我们所想的不同, 这就属于特殊情况,因此我们需要在这种特殊情况下,对于类模板作出特殊的处理。
这种处理就称为:特化,特化就意味着特殊的处理化,在原模板的基础来,对于传入的特殊类型进行特殊化的实现方式,又因为模板分为函数模板和类模板,因此特化也分为函数特化和类特化。
函数特化的基本步骤:
根据上述步骤,我们就可以写出针对特定类型的函数了:
这样就完成了对于特殊类型的特殊化处理。
什么是全特化?
全特化就是模板参数列表中的参数全部确定化
template
class Data
{
public:
Data()
{
cout << "Data" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//全特化
template<>
class Data
{
public:
Data()
{
cout << "Data" << endl;
}
private:
int _d1;
char _d2;
};
void TestVector()
{
Data d1;
Data d2;
}
int main()
{
TestVector();
return 0;
}
我们发现当传入的类型是特定类型时,就会采用特殊的模板生成的类。
什么是偏特化?
偏特化指的就是模板参数列表中的一些参数确定化
template
class Data
{
public:
Data()
{
cout << "Data" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//偏特化
template
class Data
{
public:
Data()
{
cout << "Data" << endl;
}
private:
int _d1;
char _d2;
};
void TestVector()
{
Data d1;
Data d2;
}
int main()
{
TestVector();
return 0;
}
当传入的参数的第二个位置只要是char就会调用偏特化的类模板生成的类。
偏特化有两大应用:
1. 将模板函数列表中的一些参数进行特化
2. 可以对传入参数的进一步限制
//两个参数偏特化为指针类型
template
class Data
{
public:
Data()
{
cout << "Data" << endl;
}
private:
T1 _d1;
T2 _d2;
};
//两个参数偏特化为引用类型
template
class Data
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout << "Data" << endl;
}
private:
const T1& _d1;
const T2& _d2;
};
当传入的参数类型是指针或者引用时,会调用特定的类模板生成的类。
优点:
1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2. 增强了代码的灵活性
缺点:
1. 模板会导致代码膨胀问题,也会导致编译时间变长
2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误