C++模板(函数模板类模板)
Posted yumoz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++模板(函数模板类模板)相关的知识,希望对你有一定的参考价值。
为什么要有模板?
如果要交换两个值,如果两位数都是(int,int)则需要调用下面第一个函数void Swap(int &left, int &right)
,如果要交换的是(double,double)则要调用的是void Swap(double &left, double &right)
。那么如果需要调用别的float,long等类型就需要在此实现函数重载,虽然也可以实现,但是相对代码的复用率比较低,代码维护性也比较低效。于是就有了泛函编程。
void Swap(int &left, int &right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double &left, double &right)
{
double temp = left;
left = right;
right = temp;
}
泛函编程:编写与类型没有关系的通用代码,是代码复用的一种手段。模板作为泛函编程的基础。对于模板有以下两种,函数模板和类模板。
函数模板
函数模板是一个函数家族,该函数模板与类型无关,在使用过程中被参数化,根据调用的实参类型产生函数的特定类型的版本。
函数模板的格式
template <typename T1, typename T2, ... , typename Tn>
返回值类型 函数名 (参数列表){}
这里将上述的交换函数写成函数模板为下面形式:
template<typename T> //定义模板参数T可使用typename或class
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
注释:typename可以换成class 但是 class不能换成struct。
函数模板原理
函数模板本质不是函数,是编译器用使用方式产生特定具体类型函数的模具。模板也就是说将我们该做的重复的事交给编译器去完成。
查看下图,仔细分析发现调用函数的模板实现交换,但是调用函数的地址不一样,可以说明低层调用的是不同的函数。
总结:
在编译器阶段,对于模板函数的使用,编译器是依据传入的实参类型来推演出对应类型的函数以供调用。(当我们使用int类型去调用函数模板时,编译器根据实参类型推演,将T确定为int类型,然后产生一份用来处理int类型的代码,就如上图call不同的地址的函数)。
函数模板实例化
函数模板实例化:是用不用类型的参数使用函数模板。
模板参数实例化:
- 隐式实例化
- 显示实例化。
隐式实例化:
(编译器根据实参推演模板参数的实际类型)
显示实例化:
(语法:函数名<指定的类型>(实参列表) 。)
模板参数匹配
- 一个非模板函数可以和函数模板同时存在,该函数模板仍然可以被实例化为这个非模板函数。
- 对于非模板函数和函数模板同时存在时,在同样情况下,一般情况下,优先选择调用非模板函数,而不用选用函数模板来去实例化一个函数,除非此函数模板可以实例化出来更好的匹配的函数。
简述:有现成的匹配的函数,就调用现成的。实例化的更合适就选择匹配实例化出来的函数。 - 函数模板不允许自动类型的转换,这点普通函数可以实现自动类型转换。
类模板
定义格式
template <class T1,class T2,...,class Tn>
class 类模板名
{
//类内的成员函数和成员变量
}
类模板实现
namespace yumoz
{
template <class T>
class vector
{
public:
vector()
:_a(nullptr)
, _size(0)
, _capacity(0)
{}
~vector()
{
delete[] _a;
_a = nullptr;
_size = _capacity = 0;
}
void push_back(const T& x)
{
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
T* temp = new T[newcapacity];
if (_a)
{
memcpy(temp, _a, sizeof(T)*_size);
delete[] _a;
}
_a = temp;
_capacity = newcapacity;
}
_a[_size] = x;
++_size;
}
//类内实现
/* T& operator[](size_t pos)
{
assert(pos < _size);
return _a[pos];
}
size_t size()
{
return _size;
}*/
//类外实现 部分1
T& operator[](size_t pos);
size_t size();
private:
T* _a;
size_t _size;
size_t _capacity;
};
//类外实现 部分2
template<class T>//不可省略
T& vector<T>::operator[](size_t pos)
{
assert(pos < _size);
return _a[pos];
}
template<class T>//不可省略
size_t vector<T>::size()
{
return _size;
}
}
int main()
{
yumoz::vector<int>v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
//v1.operator[](3)
for (size_t i = 0; i < v1.size(); ++i)
{
v1[i] *= 3;//扩大三倍,返回值是引用
}
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
yumoz::vector<double>v2;
v2.push_back(1.1);
v2.push_back(2.2);
for (size_t i = 0; i < v2.size(); ++i)
{
cout << v2[i] << " ";
}
cout << endl;
return 0;
}
类模板实例化
类模板实例化需要在类模板名字后跟上<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果是真正的类。
vector<int>v1;
vector<double>v2;
以上是关于C++模板(函数模板类模板)的主要内容,如果未能解决你的问题,请参考以下文章