模版
一.泛型编程
当我们要交换两个变量时,可以使用函数重载,如下:
void Swap(int& x, int& y)
{}
void Swap(double& x, double& y)
{}
void Swap(char& x, char& y)
{}
但是函数重载也有不好的地方:
-
重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增
加对应的函数。 -
代码的可维护性比较低,一个出错可能所有的重载均出错。
那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?于是有了泛型编程。
泛型编程
:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
。
二.函数模版
1.函数模版的概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
2.函数模板的格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
template<typename T>
void Swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
注意:typename
是用来定义模板参数关键字,也可以使用class
(切记:不能使用struct代替class)
3.函数模版的原理
函数模板是一个模具,它本身并不是函数
,是编译器用使用方式产生特定具体类型函数的模具
。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码。
4.函数模版的实例化
用不同类型的参数使用函数模板时
,生成对应的函数
,称为函数模板的实例化
。模板参数实例化分为:隐式实例化
和显式实例化
。
- 隐式实例化:让编译器根据实参推演模板参数的实际类型。
- 显式实例化:在函数名后的<>中指定模板参数的实际类型。
template<class T>
T Add(const T& x, const T& y)
{
return x + y;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 1.1, d2 = 1.2;
//隐式实例化
cout << Add(a1, a2) << endl;
cout << Add(d1, d2) << endl;
//Add(a1, d2);编译器无法确定此处到底该将T确定为int 或者 double类型而报错
//推导实例化
cout << Add(a1, (int)d1) << endl; //11
cout << Add((double)a1, d1) << endl; //11.1
//显示实例化
cout << Add<int>(a1, d1) << endl; //11
cout << Add<double>(a1, d1) << endl; //11.1
return 0;
}
必须显示实例化的案例:
template<class T>
T* func(int n)
{
return new T[n];
}
int main()
{
//int* p = func(10);//无法为 "T" 推导模版参数,编译报错
int* p = func<int>(10);//必须显示实例化
return 0;
}
5.模板参数的匹配原则
- 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这
个非模板函数。 - 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而
不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模
板。 - 模板函数不允许自动类型转换(编译报错),但普通函数可以进行自动类型转换。
template<class T>//模版参数
T Add(const T& x, const T& y)//函数模版
{
return x + y;
}
int Add(const int& x, const int& y)
{
return (x + y) * 2;
}
int main()
{
int a = 10;
int b = 20;
double d = 1.1;
cout << Add(a, b) << endl; //调用现成的Add函数——>60
cout << Add<int>(a, b) << endl; //调用模版——>30
cout << Add(a, d) << endl; //调用现成的Add函数,发生隐式类型转换——>22
cout << Add(a, d) << endl; //若没有现成的Add函数——>编译错误
return 0;
}
三.类模版
1.类模板的定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
模版参数只能给当前类模版使用
,使用一次,不能多次使用,同理模版参数也不能多次给函数模版使用,比如:
template<class T>
class Stack
{
public:
Stack(int n = 4)
:_array(new T[n])
,_size(0)
,_capacity(n)
{
cout << "Stack(int n = 4)" << endl;
}
void Push(const T& x);
//void Push(const T& x)
//{
// if (_size == _capacity)
// {
// //C++需要手动扩容
// T* tmp = new T[_capacity * 2];
// memmove(tmp, _array, sizeof(T) * _size);
// delete[] _array;
// _array = tmp;
// _capacity *= 2;
// }
// _array[_size++] = x;
//}
~Stack()
{
delete[] _array;
_array = nullptr;
_size = _capacity = 0;
cout << "~Stack()" << endl;
}
private:
T* _array;
size_t _size;
size_t _capacity;
};
//模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误
template<class T>//声明模版参数
void Stack<T>::Push(const T& x)//指定作用域
{
void Push(const T& x)
{
if (_size == _capacity)
{
//C++需要手动扩容
T* tmp = new T[_capacity * 2];
memmove(tmp, _array, sizeof(T) * _size);
delete[] _array;
_array = tmp;
_capacity *= 2;
}
_array[_size++] = x;
}
}
int main()
{
//类模版:都是显示实例化,实例化出不同的类型
Stack<int> st1; //int
st1.Push(1);
st1.Push(2);
st1.Push(3);
Stack<double> st2; //double
st2.Push(1.1);
st2.Push(2.2);
st2.Push(3.3);
Stack<int>* pst = new Stack<int>;
delete pst;
return 0;
}
2.类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类
,而实例化的结果才是真正的类
。
//Stack是类名,Stack<int>才是类型
Stack<int> st1; //int
Stack<double> st2; //double
//Stack<int>与Stack<double>是不同的类型
标签:初阶,函数,int,模版,C++,实例,Stack,模板
From: https://blog.csdn.net/2203_76003626/article/details/140590190