在C++中,模板是一种强大的泛型编程工具,它允许我们编写可以处理不同类型数据的函数或类,而无需为每种数据类型重复编写代码。模板分为函数模板和类模板,它们通过参数化的方式,使得代码具有更好的通用性和复用性。
一、函数模板
函数模板允许函数的参数类型是泛型的,即在函数定义时,参数的类型并不确定,只有在调用函数时才确定具体的类型。
1. 函数模板的基本定义
模板函数通常使用template
关键字来定义,后面紧跟模板参数声明,使用时由编译器推导参数类型。
#include <iostream>
// 定义一个模板函数,用于返回两个值中的较大者
template <typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
int a = 10, b = 20;
double c = 5.6, d = 3.2;
std::cout << "较大的整数是: " << getMax(a, b) << std::endl; // 自动推导为 int
std::cout << "较大的浮点数是: " << getMax(c, d) << std::endl; // 自动推导为 double
return 0;
}
输出结果:
较大的整数是: 20
较大的浮点数是: 5.6
2. 显式指定模板参数
虽然通常情况下,编译器可以根据实参自动推导模板参数类型,但在某些情况下,你可以显式指定模板参数类型。
#include <iostream>
template <typename T>
T getMin(T a, T b) {
return (a < b) ? a : b;
}
int main() {
int x = 100, y = 200;
double p = 10.5, q = 20.5;
// 显式指定模板参数为 int 类型
std::cout << "较小的值 (int): " << getMin<int>(x, y) << std::endl;
// 显式指定模板参数为 double 类型
std::cout << "较小的值 (double): " << getMin<double>(p, q) << std::endl;
return 0;
}
输出结果:
较小的值 (int): 100
较小的值 (double): 10.5
3. 模板的重载与特化
模板函数可以像普通函数一样被重载,同时也可以通过特化来为特定类型提供定制化的实现。
(1) 重载模板
#include <iostream>
template <typename T>
T getAbs(T a) {
return (a < 0) ? -a : a;
}
// 重载:专门处理浮点类型
double getAbs(double a) {
std::cout << "处理浮点类型" << std::endl;
return (a < 0) ? -a : a;
}
int main() {
int i = -10;
double d = -5.6;
std::cout << "整数绝对值: " << getAbs(i) << std::endl;
std::cout << "浮点数绝对值: " << getAbs(d) << std::endl;
return 0;
}
输出结果:
整数绝对值: 10
处理浮点类型
浮点数绝对值: 5.6
(2) 模板特化
有时我们希望为某个特定类型提供不同的实现,这时可以使用模板特化。
#include <iostream>
// 通用版本的模板
template <typename T>
T getAbs(T a) {
return (a < 0) ? -a : a;
}
// 针对 char 类型的特化版本
template <>
char getAbs(char a) {
std::cout << "字符绝对值并不适用" << std::endl;
return a;
}
int main() {
int i = -10;
char ch = 'A';
std::cout << "整数绝对值: " << getAbs(i) << std::endl;
std::cout << "字符绝对值: " << getAbs(ch) << std::endl;
return 0;
}
输出结果:
整数绝对值: 10
字符绝对值并不适用
字符绝对值: A
二、类模板
类模板类似于函数模板,它允许我们创建泛型类。在类模板中,我们可以定义成员函数和成员变量的类型是模板参数。
1. 类模板的基本定义
类模板同样使用template
关键字来定义,模板参数可以应用于类的成员函数、成员变量等。
#include <iostream>
// 定义一个通用的 Pair 类模板,用于存储两个不同类型的值
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(T1 a, T2 b) : first(a), second(b) {}
void display() const {
std::cout << "First: " << first << ", Second: " << second << std::endl;
}
};
int main() {
Pair<int, double> p1(10, 3.14);
Pair<std::string, char> p2("Hello", 'A');
p1.display();
p2.display();
return 0;
}
输出结果:
First: 10, Second: 3.14
First: Hello, Second: A
2. 类模板的成员函数定义
类模板的成员函数可以直接在类内定义,也可以像普通类一样在类外定义。
#include <iostream>
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
// 构造函数
Pair(T1 a, T2 b) : first(a), second(b) {}
// 成员函数在类外定义
void display() const;
};
// 类外定义成员函数
template <typename T1, typename T2>
void Pair<T1, T2>::display() const {
std::cout << "First: " << first << ", Second: " << second << std::endl;
}
int main() {
Pair<int, double> p(42, 6.28);
p.display();
return 0;
}
输出结果:
First: 42, Second: 6.28
3. 类模板的继承
类模板可以继承另一个类模板或普通类,甚至可以实现多重继承。
#include <iostream>
// 基类模板
template <typename T>
class Base {
protected:
T value;
public:
Base(T v) : value(v) {}
void display() const {
std::cout << "Base value: " << value << std::endl;
}
};
// 派生类模板
template <typename T>
class Derived : public Base<T> {
public:
Derived(T v) : Base<T>(v) {}
void show() const {
std::cout << "Derived value: " << this->value << std::endl;
}
};
int main() {
Derived<int> obj(100);
obj.display(); // 调用基类方法
obj.show(); // 调用派生类方法
return 0;
}
输出结果:
Base value: 100
Derived value: 100
三、模板的使用场景
模板广泛用于C++标准库和第三方库中,尤其是在容器、算法、智能指针等泛型编程中。C++ STL(标准模板库)中,容器如vector
、list
、map
等,都是基于模板实现的。
1. 标准模板库(STL)中的模板
例如,std::vector
是C++标准库中的动态数组,它是一个类模板,可以存储任意类型的元素。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
for (int v : vec) {
std::cout << v << " ";
}
std::cout << std::endl;
return 0;
}
输出结果:
1 2 3 4 5
四、总结
C++模板是泛型编程的核心工具之一,通过函数模板和类模板,我们可以编写通用性极强的代码,大大减少重复代码的编写。函数模板使函数可以接受不同类型的参数,而类模板则允许类对不同数据类型进行操作。模板是现代C++编程不可或缺的一部分,掌握模板将大大提升代码的