交换两个数 任何一个类型交换还要重新写一个函数 如何解决?
模板->写跟类型无关的函数
1.泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
如何写一个函数适用所有类型的交换?
#include <iostream>
using namespace std;
template<class T>
//template<typename T> //模板参数 -> 类型
void Swap(T& x1, T& x2)
{
T x = x1;
x1 = x2;
x2 = x;
}
//下面调用的是否是同一个函数
//不是
//这里我们不能调用函数模板 调用的是函数模板
//实例化生成的对应类型的函数
int main()
{
int a = 0, b = 1;
Swap(a, b);
double c = 1.11, d = 2.22;
Swap(c, d);
char e = 'a', f = 'b';
Swap(e, f);
cout << a << " " << b << endl
<< c << " " << d << endl
<< e << " " << f << endl;
return 0;
}
2.函数模板
1°概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
2°格式
template<typename T1, typename T2,......,typename Tn> 返回值类型 函数名(参数列表){}
3°原理
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。
比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
4°实例化
写了模板后调用 -> 实例化
int x1,x2;
swap(x1,x2); void swap(int& a,int& b)
double x3,x4;
swap(x3,x4); void swap(double& a,double& b)
stack<int> st1; class stack{int* _a;...}
stack<double> st2; class stack{double* _a;...}
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。
- 隐式实例化
- 显式实例化
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;
// 隐式实例化 (T的类型是编译器自己推导的)
Add(a1, a2);
Add(d1, d2);
//T要么全int 要么全double
//Add(a1, d1);
//解决
//1.强转:
Add(a1, (int)d1);
//2.显示实例化(指定T的类型)
//类模板也是这样
Add<int>(a1, d1);
return 0;
}
显示实例化:类似于Add 指定了类型
5°模板的匹配
- 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
//专门处理int的加法函数 外卖
int Add(int left, int right)
{
return left + right;
}
// 通用模板 自己做
template<class T>
T& Add(const T& left, const T& right)
{
return left + right;
}
void main()
{
Add(1, 2); //与非模板函数匹配 编译器不需要特化 大家都会优先选择点外卖
Add<int>(1, 2); //调用编译器特化的Add版本 指定了自己做
}
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板.
//专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函
数
}
- 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
3.类模板
#include <iostream>
#include <assert.h>
using namespace std;
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);//可以引用传 减少拷贝
void pop_back();
//[]重载 直接拿到下标为i的元素 调用的时候就和遍历数组一样
T& operator[](size_t i)
{
assert(i < _size);
return _a[i];
}
//遍历数组
size_t size()
{
return _size;
}
private:
T* _a;
size_t _size;
size_t _capacity;
};
//类外面定义写出类型vector<T>::
//与类分离了 再写一次模板
template<class T>
void vector<T>::push_back(const T& x)
{
//如果空间不够 需要进行增容
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
T* tmp = new T[newcapacity];
if (_a)
{
//拷贝
memcpy(tmp, _a, sizeof(T) * _size);
//如果本身不为空 销毁原来空间
delete[] _a;
}
//指向新的扩容空间
_a = tmp;
_capacity = newcapacity;
}
//放元素
_a[_size] = x;
//调整
++_size;
}
template<class T>
void vector<T>::pop_back()
{
assert(_size > 0);
--_size;
}
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.pop_back();
for (size_t i = 0; i < v.size(); ++i)
{
//[]写 v.operator[](i);
//如果传值返回 返回值给到v[i] 会拷贝临时变量 具有常性 不可改
//而且改变的是临时变量 不是原来的值
//传引用返回可以解决问题 返回的是引用别名
//引用传参:1.修改传递的实参 比如swap 2.减少拷贝
//引用传返回值:1.修改返回对象如operator[] 2.减少拷贝
v[i] *= 2;
}
for (size_t i = 0; i < v.size(); ++i)
{
//[]读
cout << v[i] << " ";
}
cout << endl;
return 0;
}
实例化:
Vector s1;
Vector s2;
【C++】6.模板初阶 完