1. 函数模板的概念与用法
函数模板是一种可以自动定义多个类型相似的函数的模板。它们是创建泛型函数的一种方式,使得函数可以使用多种不同类型的参数。函数模板使用关键字template来定义,后面跟上一个或多个类型参数,它们可以用来定义函数的参数类型和返回类型。例如,下面是一个使用函数模板创建的函数,用于计算两个类型相同的值之和:
template<class T>
T add(T a, T b) {
return a + b;
}
使用这个模板函数时,我们可以传递任何类型的参数。例如,我们可以将两个整数传递给这个函数,也可以将两个浮点数传递给它:
int a = 5, b = 10;
float c = 1.5, d = 2.5;
cout << add(a, b) << endl; // 输出 15
cout << add(c, d) << endl; // 输出 4
在这个例子中,我们使用了类型参数T,T可以是任何类型,整型,浮点型,甚至是自定义类型。当代码被编译器处理时,它会根据参数的具体类型自动将函数模板实例化。
1.1函数模板的显式实例化
函数模板还支持显式实例化,这意味着我们可以在程序的某个地方明确指定模板参数的类型,这样就不需要让编译器在编译时自动实例化了。这对于某些特定的应用场景非常有用,例如当我们需要逐个实例化模板函数中的所有版本时。下面是一个使用显式实例化的例子:
template<class T>
T add(T a, T b) {
return a + b;
}
// 显式实例化模板函数
template int add<int>(int a, int b);
template float add<float>(float a, float b);
在这个例子中,我们使用了显式实例化来限制模板参数的类型。这样,只有int类型和float类型的add函数被实例化,从而减少了代码的冗余并提高了编译器的效率。
1.2函数模板的特化
函数模板还支持特化,这意味着我们可以为特定的类型创建特定的函数实现。例如,下面的例子展示了如何为字符串类型创建一个特定的add函数:
template<class T>
T add(T a, T b) {
return a + b;
}
// 为字符串类型创建特化函数
template<>
string add(string a, string b) {
return a + "," + b;
}
在这个例子中,我们使用特化来为字符串类型创建一个特定的函数实现,该函数在连接两个字符串时添加了逗号。当我们使用add函数时,如果参数类型是字符串,编译器将自动选择特化版本的函数。
2. 类模板的概念与用法
类模板是一个可以自动定义多个类型相似的类的模板。它们允许我们将类的定义与存储类型分离,并为不同类型的对象提供通用的功能。类模板使用关键字template来定义,后面跟上一个或多个类型参数,这些类型参数可以用来定义类的成员变量和成员函数的参数和返回值类型。例如,下面是一个使用类模板创建的向量类:
template<class T>
class Vector {
private:
T *data;
int size;
public:
Vector(int size) {
data = new T[size];
this->size = size;
}
~Vector() {
delete[] data;
}
T &operator[](int index) {
return data[index];
}
};
在这个例子中,我们使用了类型参数T来定义向量的成员变量和成员函数的返回值类型。在运行时,当对象被创建时,编译器会根据对象的类型自动实例化类模板。例如,下面是一个使用向量类模板创建的整型向量:
Vector<int> v(10);
for (int i = 0; i < 10; i++) {
v[i] = i;
cout << v[i] << endl;
}
2.1类模板的显式实例化
类模板也支持显式实例化。和函数模板类似,我们可以在程序的某个地方明确指定模板参数的类型,然后创建该类型的对象,这样就不需要让编译器在编译时自动实例化了。下面是一个使用显式实例化的例子:
template<class T>
class Vector {
private:
T *data;
int size;
public:
Vector(int size) {
data = new T[size];
this->size = size;
}
~Vector() {
delete[] data;
}
T &operator[](int index) {
return data[index];
}
};
// 显式实例化类模板
template class Vector<int>;
在这个例子中,我们使用了显式实例化来创建一个特定类型的向量类。因此,编译器只会生成int类型的向量类,减少了代码的冗余并提高了编译器的效率。
2.2类模板的特化
类模板还支持特化,这意味着我们可以为特定的类型创建一个特定的类实现。例如,下面的例子展示了如何为字符串类型创建一个特定的向量类:
template<class T>
class Vector {
private:
T *data;
int size;
public:
Vector(int size) {
data = new T[size];
this->size = size;
}
~Vector() {
delete[] data;
}
T &operator[](int index) {
return data[index];
}
};
// 为字符串类型创建特化类
template<>
class Vector<string> {
private:
string *data;
int size;
public:
Vector(int size) {
data = new string[size];
this->size = size;
}
~Vector() {
delete[] data;
}
string &operator[](int index) {
return data[index];
}
};
在这个例子中,我们使用特化来为字符串类型创建一个特定的向量类。当我们使用Vector<string>时,编译器将自动选择特化版本的类来创建对象。
3. 总结
函数模板和类模板都是C++中非常强大的泛型编程工具。它们允许我们定义一次扩展的代码,可以适用于许多不同的数据类型。这大大减少了代码的冗余并提高了代码的可重用性。另外,函数模板和类模板还支持显式实例化和特化,从而进一步扩展了它们的功能。
在实际应用中,我们可以使用函数模板来定义一些通用的算法,例如排序、查找、统计等等。使用类模板则可以定义一些通用的数据结构,例如向量、树、堆等等。通过使用模板,我们可以写出简洁、高效、易于维护的代码。