1. 引言
C++模板是一种强大的泛型编程工具,它允许程序员编写独立于具体类型的代码。 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。 通过模板,我们可以创建通用的函数和类,这些函数和类可以适用于多种数据类型,从而提高代码的复用性和灵活性。 本文将详细介绍C++模板的基本概念和使用方法。
2. 模板的基本概念
模板是C++的一种特性,用于创建函数或类的通用形式,这些形式可以应用于多种数据类型。
模板允许开发者编写一次代码,然后用不同的类型实例化,从而生成具体的函数或类。
3. 函数模板
3.1 定义和语法
函数模板的定义使用关键字template
,后跟模板参数列表,然后是函数声明或定义。模板参数列表通常包含一个或多个类型参数。
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
注意:
typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
3.2 函数模板实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。
模板参数实例化分为:隐式实例化 和显式实例化。
3.3 隐式实例化
当调用函数模板时,编译器会根据传递的参数类型自动推导出模板参数的具体类型。
这种实例化,称为隐式实例化,如下方示例:
#include<iostream>
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
int x = 3, y = 4;
double a = 1.1, b = 2.2;
int maxInt = max(x, y); // T 被推导为 int
double maxDouble = max(a, b); // T 被推导为 double
std::cout << "Max of " << x << " and " << y << " is " << maxInt << std::endl;
std::cout << "Max of " << a << " and " << b << " is " << maxDouble << std::endl;
return 0;
}
要注意的是:如果传递的参数不能让编译器正确推导出实例化的函数,就会报错
如下方示例:
#include<iostream>
template <typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
int x = 3, y = 4;
double a = 1.1, b = 2.2;
int maxInt = max(x, a); // 编译报错
std::cout << "Max of " << x << " and " << y << " is " << maxInt << std::endl;
return 0;
}
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要 背黑锅
此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
int maxInt = max(x, (int)a); //强制类型转换
3.4 显式实例化
显式指定模板参数的类型,可以使用尖括号<>
来指定。
int main() {
int x = 3, y = 4;
double a = 1.1, b = 2.2;
int maxInt = max<int>(x, y); // 显式指定 T 为 int
double maxDouble = max<double>(a, b); // 显式指定 T 为 double
std::cout << "Max of " << x << " and " << y << " is " << maxInt << std::endl;
std::cout << "Max of " << a << " and " << b << " is " << maxDouble << std::endl;
return 0;
}
3.5 模板函数的匹配规则
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非模板函数匹配,编译器不需要特化
Add<int>(1, 2); // 调用编译器特化的Add版本
}
4. 类模板
4.1 定义和语法
类模板的定义类似于函数模板,使用关键字template
,后跟模板参数列表,然后是类的定义。
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
示例代码:
template <typename T>
class Stack {
private:
T* items; // 数组存储元素
int top; // 栈顶索引
int size; // 栈的最大容量
public:
Stack(int sz) : size(sz), top(-1) {
items = new T[size];
}
~Stack() {
delete[] items;
}
void push(const T& item);
T pop();
bool isEmpty() const;
};
4.2 成员函数的定义
类模板的成员函数可以在类内定义,也可以在类外定义。
注意:模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误
// 在类内定义
template <typename T>
class Stack {
public:
void push(const T& item) {
if (top < size - 1) {
items[++top] = item;
} else {
std::cout << "Stack overflow" << std::endl;
}
}
T pop() {
if (top >= 0) {
return items[top--];
} else {
std::cout << "Stack underflow" << std::endl;
return T(); // 返回默认构造的对象
}
}
bool isEmpty() const {
return top == -1;
}
};
// 在类外定义
template <typename T>
void Stack<T>::push(const T& item) {
if (top < size - 1) {
items[++top] = item;
} else {
std::cout << "Stack overflow" << std::endl;
}
}
template <typename T>
T Stack<T>::pop() {
if (top >= 0) {
return items[top--];
} else {
std::cout << "Stack underflow" << std::endl;
return T(); // 返回默认构造的对象
}
}
template <typename T>
bool Stack<T>::isEmpty() const {
return top == -1;
}
4.3 模板参数的默认值
模板参数可以有默认值,这样在实例化时可以省略某些参数。
template <typename T, int Size = 100>
class Stack {
private:
T* items; // 数组存储元素
int top; // 栈顶索引
int size; // 栈的最大容量
public:
Stack() : size(Size), top(-1) {
items = new T[size];
}
~Stack() {
delete[] items;
}
void push(const T& item);
T pop();
bool isEmpty() const;
};
注意:
模板参数可以是类型参数(如 typename T
)或非类型参数(如 int N
)。
非类型参数必须是编译时常量表达式,并且其类型通常是整型、枚举类型、指针类型或引用类型(指向对象的引用)
这里的 int Size = 100
实际上是一个非类型模板参数,它有一个默认值。这意味着当实例化这个模板类时,如果不提供第二个参数,Size
将默认为100。但是,请注意,这个参数是模板级别的,而不是对象级别的。也就是说,一旦模板被实例化(例如 Stack<int>
或 Stack<int, 200>
),该实例的 Size
值就是固定的,不能在运行时改变
5. 模板的高级用法
5.1 模板特化
模板特化允许为特定的类型提供特殊的实现。
// 全特化
template <>
class Stack<char> {
private:
char* items; // 数组存储元素
int top; // 栈顶索引
int size; // 栈的最大容量
public:
Stack(int sz) : size(sz), top(-1) {
items = new char[size];
}
~Stack() {
delete[] items;
}
void push(char item);
char pop();
bool isEmpty() const;
};
// 部分特化(仅限类模板)
template <typename T, int Size>
class Stack<T*, Size> {
private:
T** items; // 数组存储元素
int top; // 栈顶索引
int size; // 栈的最大容量
public:
Stack() : size(Size), top(-1) {
items = new T*[size];
}
~Stack() {
delete[] items;
}
void push(T* item);
T* pop();
bool isEmpty() const;
};
5.2 模板模板参数
模板模板参数允许将一个模板作为另一个模板的参数。
template <template <typename, int> class Container, typename T, int Size>
class MyClass {
Container<T, Size> container;
public:
void add(const T& item) {
container.push(item);
}
T remove() {
return container.pop();
}
};
6. 实战案例
6.1 函数模板示例
编写一个通用的数组排序函数。
#include <algorithm>
template <typename T>
void sortArray(T arr[], int size) {
std::sort(arr, arr + size);
}
int main() {
int intArr[] = {5, 2, 8, 1, 9};
double doubleArr[] = {3.1, 2.2, 1.1, 5.5, 4.4};
sortArray(intArr, 5);
sortArray(doubleArr, 5);
for (int i : intArr) {
std::cout << i << " ";
}
std::cout << std::endl;
for (double d : doubleArr) {
std::cout << d << " ";
}
std::cout << std::endl;
return 0;
}
6.2 类模板示例
编写一个通用的链表类。
template <typename T>
class LinkedList {
private:
struct Node {
T data;
Node* next;
Node(T val) : data(val), next(nullptr) {}
};
Node* head;
public:
LinkedList() : head(nullptr) {}
~LinkedList() {
clear();
}
void insert(T value);
void remove(T value);
bool contains(T value) const;
void clear();
};
template <typename T>
void LinkedList<T>::insert(T value) {
Node* newNode = new Node(value);
if (!head) {
head = newNode;
} else {
Node* current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
}
}
template <typename T>
void LinkedList<T>::remove(T value) {
Node* current = head;
Node* previous = nullptr;
while (current) {
if (current->data == value) {
if (previous) {
previous->next = current->next;
} else {
head = current->next;
}
delete current;
return;
}
previous = current;
current = current->next;
}
}
template <typename T>
bool LinkedList<T>::contains(T value) const {
Node* current = head;
while (current) {
if (current->data == value) {
return true;
}
current = current->next;
}
return false;
}
template <typename T>
void LinkedList<T>::clear() {
Node* current = head;
while (current) {
Node* next = current->next;
delete current;
current = next;
}
head = nullptr;
}
int main() {
LinkedList<int> list;
list.insert(1);
list.insert(2);
list.insert(3);
std::cout << "Contains 2: " << list.contains(2) << std::endl;
list.remove(2);
std::cout << "Contains 2: " << list.contains(2) << std::endl;
return 0;
}
7. 常见问题与注意事项
8. 结语
C++模板是实现泛型编程的重要手段,它不仅增强了代码的复用性和可维护性,还提高了程序的执行效率。通过本文的学习,希望读者能够掌握C++模板的基本概念和使用方法,并在实际编程中灵活运用。
9. 参考文献
希望这篇文章对你有所帮助,祝你在学习C++模板的过程中取得更大的进步!