文章目录
- 泛型编程
- 函数模板
- 格式
- 模板调用的是同一个函数吗?
- 模板的实现原理
- T不明确
- 模板实例化的函数和普通函数
- 类模板
- 类模板写法
- 类模板用法
- 注意事项
泛型编程
假如我们要写一个两数交换的函数,按我们之前学的知识,我们会这样。
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;
}
int main()
{int a = 1, b = 2;//Swap(a, b);Swap(a, b);double c = 1.1, d = 2.22;//Swap(a, b);Swap(c, d);return 0;
}
这是函数重载,按我们的理解,再增加不同的类型的数进行交换又要写一个函数,这样就需要不断的去写函数。
所以这里就引出了我们接下来要说的泛型编程。
函数模板
格式
template<typename T>
template是关键字,
T是模板类型名称,可以随便给。
定义多个模板参数
template<typename x, typename y>
上面交换的代码可以写成这样
template<class T>
void Swap(T& x, T& y)
{T tmp = x;x = y;y = tmp;
}
int main()
{int a = 1, b = 2;//Swap(a, b);Swap(a, b);double c = 1.1, d = 2.22;//Swap(c,d);Swap(c, d);return 0;
}
它是针对广泛的类型来进行编程的。T具体的类型是什么我也不知道。
写成这样,无论什么类型的数据进行交换都只需要写这一个模板。
模板调用的是同一个函数吗?
那现在问题来了,Swap(a, b);和Swap(c, d);调用的是同一个函数吗?
通过调试我们发现好像是这样,但是只看这个是不够的,还是看一下汇编,结果发现函数地址不一样,所以肯定不是同一个函数。
其实我们仔细想想也知道,不可能是同一个,同一个指令是一样的。那函数要建立栈帧,这两个栈帧大小一样吗?
要交换的数的类型不一样,函数栈帧的大小也肯定不一样。
模板的实现原理
那现在又有一个问题,上面两个swap函数调用的是不是模板?
其实不是,这么说吧!Swap(a, b);编译器会判断出a,b的类型是int,继而对模板进行一定的推演,判断出T的类型是int,然后根据模板实例化一个用int数据交换的函数。实际函数调用的不是模板,而是调用的是模板生成的代码。
其实函数一点也没有减少,只是有了模板编译器帮我们生成了。
T不明确
下面看这段代码会报错吗?
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 10;double d1 = 10.11;Add(a1, d1);return 0;
}
答案是会报错,这是因为模板再推演实例化的时候出现了歧义,他不知道T的类型是int还是double;
那怎么办呢?
很简单,强制类型转换。这是根据实参传递给形参,自动推演模板类型
Add(a1, (int)d1);
Add((double) a1, d1);
那除了强制类型转换还有没有别的方法呢?
显示实例化
Add<int>(a1, d1)Add<double>(a1, d1)
模板实例化的函数和普通函数
int Add(int left, int right)
{return left + right;
}// 通用加法函数
template<class T>
T Add(T left, T right)
{return left + right;
}
请问能同时存在吗?
能!
那编译器会调用哪个函数?
编译器有个原则,调用谁的成本低就调用谁。很显然如果调用模板实例化的函数需要推演实例化这些东西,所以它会调用普通函数。
那如果非要调用模板实例化的函数呢?
显示实例化
编译器自己选择?
其实编译器非常聪明,它会调用跟它更加匹配的那个函数。
类模板
类模板就是定义一个模板参数,整个类里面都可以用。
那现在问题来了,之前学习的typedef不够用吗?
答案是不够,比如写一个栈,
typedef int STDateType;
class Stack
{
private:STDateType* _a;size_t _top;size_t _capacity;
};
int main()
{Stack st;return 0;
}
现在我栈上既要存储int又要存储double数据就办不到了!
类模板写法
template<class T>
class Stack
{
public:Stack(int capaicty = 4){_a = new T[capaicty];_top = 0;_capacity = capaicty;}~Stack(){delete[] _a;_capacity = _top = 0;}private:T* _a;size_t _top;size_t _capacity;
};
类模板用法
怎么用呢?必须得显示实例化。
int main()
{Stack<int> st1; // intStack<double> st2; // doublereturn 0;
}
注意事项
1.模板的名字不能直接表示类型,加上模板参数才能表示类型
vector <int> v1;
2.类里面如果声明和定义分离怎么写?
class Vector
{~Vector();
};
template<class T>
Vector<T>::~Vector()//不能只指定类名,得指定类型
{delete[] _pData;_pData = nullptr;_size = _capacity = 0;
}
3.类模板声明和定义一定要放在同一个文件。
如果声明和定义分离会出现链接错误。