模板
函数模板
函数和类有助于使程序更易于编写,更安全,更易于维护。
然而,虽然函数和类确实具有所有这些优点,但在某些情况下,它们也可能受到C++规则的限制,必须为所有参数指定类型。
例如,你可能想写一个函数来计算两个数字的总和,就像这样:
int sum(int a, int b) {
return a+b;
}
我们现在可以在我们的main中调用两个整型函数。
int main () {
int x=6, y=16;
cout << sum(x, y) << endl;
}
// 输出 22
每一个新函数都需要声明类型,比如double型
double sum(double a, double b) {
return a+b;
}
如果能编写一个能处理任何类型参数的sum()函数,效率会不会更高呢?
函数模板使我们有能力做到这一点!
使用函数模板的基本思想是避免为每个变量指定确切的类型。相反,C ++为我们提供了使用占位符类型(称为模板类型参数)来定义函数的功能。
函数模板的定义
要定义函数模板,请使用关键字template,然后使用template类型定义:
template <class T>
现在我们可以在函数中使用我们的通用数据类型T:
template <class T> T sum(T a, T b) {
return a+b;
}
int main () {
int x=6, y=16;
cout << sum(x, y) << endl;
}
// 输出 22
该函数返回一个泛型类型T的值,获取的两个参数也是类型T。
其他数据类型可以使用相同的函数,例如double型:
template <class T> T sum(T a, T b) {
return a+b;
}
int main () {
double x=7.02, y=15.64;
cout << sum(x, y) << endl;
}
// 输出 22.66
编译器自动调用相应类型的函数。
多个通用数据类型模板
函数模板还可以处理多个通用数据类型。 使用逗号分隔需定义的数据类型。
让我们来创建一个包含多种不同数据类型的参数(一个int和一个double)的函数,然后打印一个较小的值。
template <class T, class U>
正如你所看到的,这个模板声明了两个不同的通用数据类型T和U.
现在我们可以继续我们的函数声明:
template <class T, class U> T smaller(T a, U b) {
return (a < b ? a : b);
}
(a < b ? a : b)三元运算符用法,检查a<b,如果条件成立返回a,否则返回b
int main () {
int x=70;
double y=69.99;
cout << smaller(x, y) << endl;
}
// 输出 69
最后输出是int型的,应为我们在调用函数模板的时候声明了int型函数。
上述例子中的T仅仅只是一个参数名,type的缩写。在定义参数的时候你可以取一个你自己适用的名字。
但是要记住,当你定义了一个模板参数后,你必须在函数定义中使用它,不然编译器会报错。
函数模板总结
-
函数模板可以节省很多时间,因为它们只写一次,但是可以被不同类型调用。
-
函数模板减少了代码维护,因为重复代码显着减少。
-
增强安全性是使用函数模板的另一个优点,因为不需要手动复制函数和更改类型。
类模板
类模板定义
就像我们可以定义函数模板一样,我们也可以定义类模板,允许类使用模板参数的成员作为类型。
使用相同的语法来定义类模板:
template <class T> class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b) {
}
};
就像函数模板一样,您可以使用逗号分隔的列表来定义多个通用数据类型。
类的外部定义成员函数
如果您在类的外部定义成员函数,则需要使用特定的语法,例如在单独的源文件中。
您需要在类名后面的尖括号中指定泛型类型。
例如,要在类的外部定义一个成员函数bigger(),使用以下语法:
template <class T> class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b){
}
T bigger();
};
template <class T> T Pair<T>::bigger() {
return (first>second ? first : second);
}
要创建不同类型的模板类的对象,请在尖括号中指定数据类型,就像我们在定义类之外的函数时所做的那样。
在这里,我们为整数创建一个Pair对象。
Pair <int> obj(11, 22);
cout << obj.bigger();
// 输出 22
我们也可以使用相同的类来定义一个double型的对象
Pair <double> obj(23.43, 5.68);
cout << obj.bigger();
// 输出 23.43
模板特化 模板特化
要为数据类型char指定不同的行为,我们将创建一个模板特化。
template <class T> class MyClass {
public:
MyClass (T x) {
cout <<x<<"不是char类型"<<endl;
}
};
template < > class MyClass<char> {
public:
MyClass (char x) {
cout <<x<<"是char类型!"<<endl;
}
};
首先,请注意,我们在模板<>的前面添加了一个空的参数列表。 这是因为所有类型都是已知的,并且这个特化不需要模板参数,但是仍然是类模板的特化,因此需要注意这一点。
但比这个前缀更重要的是类模板名称之后的特化参数。 这个特化参数本身标识了模板类被特化的类型(char)。
在上面的例子中,第一个类是通用模板,第二个是特化。
下一步是声明不同类型的对象并检查结果:
int main () {
MyClass<int> ob1(28);
MyClass<double> ob2(5.18);
MyClass<char> ob3('w3cschool');
}
/* 输出:
28 - 不是char类型
5.18 - 不是char类型
w3cschool 是char类型!
*/
正如你所看到的,泛型模板为int和double所调用。 但是,我们的模板特化是为char数据类型调用的。
请记住,从泛型模板到特化没有成员“继承”,所以模板类特化的所有成员都必须自行定义。