【C++——模板】
引入
你是否遇到过一些方法相同但需要处理的数据不同的代码,需要多组时就会显得冗杂。就好比做不同口味的月饼,所有月饼都需要备饼皮、放馅料、模具按压定型的流程,除了包的内容不一样,其他都一样,这就是做月饼的大致模板。这些“模板内容”可以理解为程序里面对对象进行的操作流程。C++中如何通过这种流水线方式通过一段程序来规避代码冗杂问题呢?
C++模板是一种通用编程机制:
通过模板可以实现函数和类的泛型化,提高代码复用性。
在设计程序时会遇到这样一种情况:需要设计相同功能的几个类,仅仅只是需要操作的数据类型不同。例如需要创建一个数组类,该数组可能是 int 整型数组,也可能是double 类型数组等。在C++中解决类似问题的方法就是设计一个模板类。
模板分为函数模板和类模板两种主要形式。
函数模板
模板使得我们可以生成通用的函数,这些函数能够接受任意数据类型的参数,可返回任意类型的值,而不需要对所有可能的数据类型进行函数重载。这在一定程度上实现了宏的作用。它们的原型定义可以是下面两种中的任何一个:
template < class identifier>
function_declaration;
template < typename identifier>
function_declaration;
上面两种原型定义的不同之处在关键字class 或 typename的使用。
它们实际是完全等价的,因为两种表达的意思和执行都一模一样。
例题:
如果现在让你实现交换两个数的值(没说是整数还是小
数),如何实现?
- 函数重载
如果用函数重载的话只能处理与函数定义的变量类型相同的操作变量,也就是只能处理一种类型,需处理两种类型时情况如下:
void Swap(int& x, int& y) {int tmp = x;x = y;y = tmp;
}void Swap(double* x, double* y) {double* tmp = x;x = y;y = tmp;
}
- 使用模板
template<typename T> //或template<class T>
void Swap(T&x, T& y) {T tmp= x;x = y;y = tmp;
}
函数模板本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该让程序员做的重复的事情交给了编译器。
函数模板的实例化
不同类型的参数使用模板时,称为模板的实例化。模板实例化分为隐式实例化和显示实例化。
隐式实例化:让编译器根据实参推演模板参数的实际类型。如下:
template <class T,class H>
H add(T& a, H& b) {return a + b;
}int main() {int n1 = 10, n2 = 20;int r1 = add(n1, n2);cout << r1 << endl;double b1 = 1.1, b2 = 2.2;double r2 = add(b1, b2);cout << r2 << endlreturn 0;}
但是这种方法在我们输入两个变量时保证两变量类型一致才能实现,因为都是T类型的。那我们改为类型不相同的的就可以了:
template <class T,class H>
H add(T& a, H& b) { //返回类型可以是T/H,这里最后返回doublereturn a + b;
}int main() {int n1 = 10, n2 = 20;int r1 = add(n1, n2);cout << r1 << endl;double b1 = 1.1, b2 = 2.2;double r2 = add(b1, b2);cout << r2 << endl;
//新增混合类型变量的加法:double r3 = add(n1, b1);cout << r3 << endl;return 0;}
类模板
C++也可以定义类模板,使得一个类可以有基于通用类型的成员,而不需要在类生成的时候定义具体的数据类型。
定义的类可以用来存储两个任意类型(保证这两个元素类型相同的情况下,两者可为任意类型)的元素。
template <class T>
class Pair {
public:T value[2];
private:Pair(T first, T second) {value[0] = first;value[1] = second;}
};
类模型实例化
类模板实例化需要在类模板名字后面跟<>,然后将实例化的类型放在<>中。
类模板中的成员函数若是放在类外定义时,需要加模板参数列表(结合::补充具体)
template <class T>
class Test {
public://普通类:类名就是类型//类模板:类名不是类型,如该类名是:Test<T>Test(){}//类里面声明,类外面定义void setData(const T& x);void showData();
private:T num;
};template<class T>void Test<T>::setData(const T&x){num = x;}template<class T>void Test<T>::showData() {cout << num << endl;}int main() {Test<int> t1;t1.setData(100);t1.showData();return 0;}
模板别名
在写程序的过程中,总是希望写简短的代码,给一些复杂的名称简化,或者取一个简短的名字,于是又有了类型别名,用来重新定义复杂的名字。不管是模板还是非模板都需要类型别名,可以使用typedef为模板具体化指定别名。
上文提到模板名就是类名+,也就是加上实例化的类型。定义一个别名会是代码更简洁:
typedef Test<int> t;int main() {t t1;t1.setData(100);t1.showData();return 0;}
如果代码量过大的话,可能会忘记
能这些别名是什么意思,C++11新增了一项功能——使用模板提供一系列别名(模板别名):
template<class T>using t = Test<T>;int main() {t<int> t1;t1.setData(100);t1.showData();return 0;}
练习
写一个函数模板,分别求一个数组里面数据的最大值,最小值和平均值。
template <class T>
void MathHelper(T num[], int size) {T max = num[0], min = num[0], total = 0;for (int i = 0; i < size; i++) {if (num[i] > max) {max = num[i];}if (num[i] < min) {min = num[i];}total += num[i];}cout << "最大值:" << max << endl;cout << "最小值:" << min << endl;if (size == 0) {cout << "Mean:" << 0 << endl;}else {cout << "Mean:" << total / size << endl;}
}int main() {int a[10] = { 2,3,1,5,12,65,43,63,44,90 };double b[5] = { 1.1,1.9,5.2,6.3,2.1 };//函数模板不用写明类型,会自动推理出来MathHelper(a, sizeof(a) / sizeof(a[0]));MathHelper(b, sizeof(b) / sizeof(b[0]));return 0;}