目录
前言
到模版这一块了,就开始觉得C++使用起来非常方便了,可交互性很强,但是要理解其底层的实现逻辑,才能方便我们日常的调用,具有非常重要的意义,接下来我们开始模版的学习吧。
1. 泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
我们来看函数重载的代码,如下面的Swap函数:
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;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
我们实现这些函数的模版都是差不多的,只是数据类型不一样,如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件 (生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需学会使用就好了。
2. 函数模板
2.1函数模版的格式
template <typename T1, typename T2,......,typename Tn>返回值类型 函数名(参数列表){}注意点1:typename 是 用来定义模板参数 关键字 , 也可以使用 class( 切记: 不能使用struct代替class )
举个例子:
template <class T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
int a = 1000,b = 22;
double c = 1.1, d = 8.8;
char e = 'A', f = 'B';
cout << a << " " << b << endl;
cout << c << " " << d << endl;
cout << e << " " << f << endl;
Swap(a,b);
Swap(c, d);
Swap(e, f);
cout << "交换后的数值为:" << endl;
cout << a << " " << b << endl;
cout << c << " " << d << endl;
cout << e << " " << f << endl;
return 0;
}
运行结果如下;
发现调用模版是不是太爽啦!根本就不需要写那么多的重载函数,那么这里我们要思考一个问题,我们没有写这些重载函数,那这些是怎么实现的呢?是编译器调用模版进行一个个的带入测试呢?还是调用模版推演出来的函数呢?
答案是肯定的,我们是调用模版推演的函数,因为C++提出模板是为了节省程序员的时间,我们所省略的工作,只是编译器替我们完成了.
当我们调用 Swap(a,b);编译器会在底部推演出一个函数:
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
2.2模版的匹配原则
学到这了,我们脑子里面会不会产生一个奇怪的想法呀,如果我们自己定义了一个函数,模版也可以推演出一个函数模版,那编译器会调用哪一个呢?直接编写代码,来进行测试一下:
void Swap(int& left, int& right)
{
cout << "我是自己编写的" << endl;
int temp = left;
left = right;
right = temp;
}
template <class T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
int a = 1000,b = 22;
cout << a << " " << b << endl;
Swap(a,b);
cout << "交换后的数值为:" << endl;
cout << a << " " << b << endl;
return 0;
}
结果如下;
总结如下:
匹配顺序为:
- 如果有定义出来的函数,且类型完全匹配调用时实参类型,则执行定义出来的函数.
- 如果定义出来的函数,不符合,则执行模板推演.
2.3模版的显示调用
下面来介绍一个场景,我们写的都是同类型的函数交换,如果交换的两个数不同类型呢?比如int和double,我们想到了可以类型转化:强制性转换类型,比如
Swap(a,(int)b);
这里博主引入一直显示调用的方法。
显示调用的格式为:
function<Type>(a,b,...);
显示调用的例子:
template <class T>
T Add( const T& left,const T& right)
{
return left + right;
}
int main()
{
int a = 1000;
double b = 2.2;
Add<int>(a,b);
return 0;
}
总结如下:
像上面的调用方式,是不允许的,因为模板中只有一个T,但是我们传了两个类型,编译器根据模板将不知道T应该是啥类型,而解决上面的问题只有两种
- 一是强制性转换类型,比如Add
(a,(int)b);
- 二是显示使用模板,比如Add
<int>(a,b);
3. 类模板
3.1类模板的定义格式
template <class T1,class T2,...>
class class_name { };
我们用栈来写一个类的模版吧。
template <class T>
class Stack
{
public:
Stack() :data(new T* [10]), top(0), capacity(10)
{}
~Stack()
{
delete[] data;
top = capacity = 0;
}
void Push(T& a)
{}
private:
T* data;
int top;
int capacity;
};
int main()
{
Stack <char> st1; //定义一个存储char类型的栈
Stack <int> st2; //定义一个存储int类型的栈
Stack <double> st3; //定义一个存储double类型的栈
return 0;
}
注意点:
1.类模板只是一个模板,他并不属于类.
2.对类模版的成员函数进行定义时,需要加上模版参数列表。
template<class T>
void Stack<T>::Push(T& a)
{
//需要在void后面加上Stack<T>
}