模板
模板分函数模板和类模板
模板也是针对C语言不足诞生的机制。模板更贴切的名称叫泛型编程——即不在针对某一具体类型,而是能适应广泛的类型。
函数模板
比如交换函数,不在针对int类型来设置交换,而是能让广泛的类型进行交换。
c++之前的不同类型交换函数写要重复太多次:
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(模板)
然后选择下面两种格式中的一个写
template<class T>
template<typename T>
class 和 typename目前没有区别,class和typename都是关键字,但是不能这样:template<struct T>。因为struct要兼容C语言的结构。
template<typename T>
void Swap(T& left, T& right)
{
T tmp = left;
left = right;
right = tmp;
}
一个简单的交换函数模板就写好了
简单应用:
int main()
{
int a = 1, b = 2;
double c = 2.2, d = 3.3;
Swap(a, b);
Swap(c, d);//两个swap函数不是同一个函数,按f11都会进入模板是编译器优化的结果
return 0;
}
模板的本质:调用什么类型就会根据对应的类型生成一个对应的函数
根据实参类型决定形参类型的过程叫编译器的推演,推演后生成函数的过程叫模板的实例化。
通过汇编代码发现两个函数的参数和地址都不一样
swap是库中的模板,可以直接用
函数模板类型一般是编译器通过形参传递给实参推演出来的,也存在特殊情况
template<class T>
T* func(int n)
{
return new T[n];
}
int main()
{
//int* p = func(10);这样编译器没法推演T的类型
int* p1 = func<int>(10);
double* p2 = func<double>(10);
return 0;
}
如果不能推演,那么需要显式实例化,指定模板参数。
一个模板下只能跟一个函数,不能一个模板下更多个函数。
模板函数的性质和普通函数一样,可以有缺省值;模板参数也是可以有缺省参数的。比如:template<class T = char>
模板参数——传递的是类型
函数参数——传递的是对象值
类模板
C语言数据结构可以通过typedef来改变存储的类型,但是也存在硬伤。当需要两个数据结构来存储不同的类型时就存在问题了。typedef修改类型,类型都会改。
typedef int StackDateType
class Stack
{
public:
Stack(int capacity = 0)
{
_p = new StackDateType[capacity];
_capacity = capacity;
_top = 0;
}
Push(StackDateType x)
{
……
}
private:
StackDateType* _p;
int _capacity;
int _top
};
int mian()
{
Stack st1;
Stack st2;//如果st1存储int类型的数据,st2存储double类型的数据要怎么改?
return 0;
}
所以需要用到类模板,类模板的定义过程也很简单
template<class T>//T只是类名,可以任意取
class Stack
{
public:
Stack(int capacity = 0)
{
_p = new T[capacity];
_capacity = capacity;
_top = 0;
}
Push(T x)
{
……
}
private:
T* _p;
int _capacity;
int _top
};
int mian()
{
Stack<int> st1;
Stack<double> st2;//函数模板可以根据实参推演参数类型,类模板没办法推演,所以要指定存储类型
return 0;
}
函数模板和类模板的声明和定义可以分离
函数模板:
template<typename T>
void Swap(T& left, T& right);//声明
template<typename T>//在定义的地方,模板要再写一遍
void Swap(T& left, T& right)//定义
{
T tmp = left;
left = right;
right = tmp;
}
类模板:类模板主要是类的成员函数声明和定义分离
template<class T>
class Vector
{
public :
Vector(size_t capacity = 10);//成员函数的声明
void PushBack(const T& data);
void PopBack();
T& operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
template<class T>//在定义的地方,模板要再写一遍
Vector<T>::Vector(size_t capacity = 10)//定义的地方要声明所在类域,声明的方式为:Vector<T>::
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
声明和定义分离,每个定义的前面都要加上template<class 参数名>或者template<typename 参数名>
一般声明和定义分离更规范。
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1,a2);
Add(d1,d2);
return 0;
}
这是一个正常编译的相加模板
但如果这样使用模板就会报错:Add(a1,d1);因为编译器不知道T是什么类型,T不能既是int又是double。
非要这么写的话,要指定T的具体类型:Add<int>(a1,d1);或者Add<double>(a1,d1);
这样写会报警告,被动转换(隐式类型转换)可能会丢失数据
或者主动转换(强转):Add(a1,(int)d1);或者Add((double)a1,d1);
int Add(const int& left, const int& right)
{
return left + right;
}
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
return 0;
}
模板和函数能同时存在吗?
可以
如果存在Add(1,2);语句,会调用模板函数还是一般函数?
一般函数。编译器调用时,有对应函数就调用对应函数,没有对应函数才会通过模板转换。
如果非要使用模板转换的函数呢?
要指定显式实例化:Add<int>(1, 2);
模板在哪个阶段处理?预处理?编译?汇编?
多个模板参数的情况:
template<class K, class V>
void Size(const K& key, const V& value)
{
cout << sizeof(key) << endl;
cout << sizeof(value) << endl;
}
int main()
{
Size(1, 1);
Size(1, 1.1);
Size<int, char>(1, 'A');//多个模板参数也可以指定实例化参数
return 0;
}
多个模板参数就可以推出不同类型了。
模板参数也可以有缺省值,但是要在模板函数没有参数的情况下
template<class K, class V = double>//模板参数的缺省和一般函数一样,要从右往左缺省,因为函数实参是从左往右赋值的
void Size(const K& key, const V& value)
{
cout << sizeof(key) << endl;
cout << sizeof(value) << endl;//打印出1
}
int main()
{
Size<int>(1, 'A');
return 0;
}
在模板函数有参数的情况下,还是会根据实参来推导类型
模板函数没有参数时:
template<class K, class V = double>
void Size()
{
cout << sizeof(K) << endl;//打印出4
cout << sizeof(V) << endl;//打印出8
}
int main()
{
Size<int>();
return 0;
}