引入
整数类型和浮点数类型求绝对值的算法,需要写两种重载函数吗?
int abs(int x) { return x < 0 ? -x : x; } double abs(double x) { return x < 0 ? -x : x; }
答案是不需要的
函数模板
就像制造业的模板一样,设定一个模板,大体的事情就被框定了
针对函数来讲,既然干的事情都是一样的,那么这件事就被框定了,变化的部分就是模板外的修饰
template <typename T> // 1 T abs(T x) { // 2 return x < 0 ? -x : x; }
1):typename 可以换成关键字 class; T 是自定义名字,可以设定为比较有意义的
2):依据模板参数由编译器自动生成代码
注意:不是简单的宏替换,当然你可以看作是宏替换来简单理解模板实际代码
基本数据类型
如果可以推导出 T 是基本数据类型,那么一般的cout << 这类插入运算符没有问题
自定义数据类型
特别要注意处理插入运算符,需要重载该运算符,否则没有办法“替换”
示例:
template <class T>
void outputArray(const T *array, int count) {
for(int i=0; i<cout; i++)
cout << array[i] << " ";
cout << endl;
}
// 使用部分
int a[8] = {1,2,3,4,5,6,7,8};
double b[8] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8};
char c[20] = "Welcome";
// 基本数据类型,可以自动推导出数据类型
outputArray(a, 8);
outputArray(b, 8);
outputArray(c, 20);
// 自定义类型,比如类,需要提供 << 运算符重载
注意事项
- 一个函数模板并非自动可以处理所有类型的数据
- 只有能够进行函数模板中运算的类型,可以作为类型实参
- 自定义的类,需要重载模板中的运算符,才能作为类型实参
类模板
既然函数能做成模板,那么类为什么不可以呢?
使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员,某些成员函数的参数,某些成员函数的返回值,能取多种不同类型(包括基本类型的和用户自定义类型)
template <模板参数表>
class 类模板名
{ 类成员声明 }
- 模板参数表中参数可以声明为该模板类的友元类
- 可以通过 typedef 或 using 对实例化的类模板定义别名
模板的默认实参
函数/类 模板可以有默认模板实参
template <typename T = double>
示例:
#include <iostream>
#include <cstdlib>
using namespace std;
struct Student {
int id;
float gpa;
};
template <class T>
class Store {
private:
T item;
bool haveValue;
public:
friend T;
Store();
T &getElem();
void putElem(const T &x);
};
template <class T>
Store<T>::Store():haveValue(false) {}
template <class T>
T &Store<T>::getElem() {
if(!haveValue) {
cout << "No item present!" << endl;
exit(1);
}
return item;
}
template <class T>
void Store<T>::putElem(const T &x) {
haveValue = true;
item = x;
}
int main() {
using IntStore = Store<int>; // 1
IntStore s1, s2;
s1.putElem(3);
s2.putElem(-7);
cout << s1.getElem() <<" " << s2.getElem() << endl;
Student g = {1000, 23};
Store<Student> s3; // 2
s3.putElem(g);
cout << "The student id is " << s3.getElem().id << endl;
Store<double> d;
cout << "Retrieving object D...";
cout << d.getElem() << endl;
return 0;
}
1):使用 using 方式可以定义别名
2):不实用 using 方式
再次说明,可以看作是 宏替换,但具体是编译器做替换动作,否则这个文件会出现重复定义类的情况,这也说明了不是简单 宏替换的做法