博客主页:倔强的石头的CSDN主页
Gitee主页:倔强的石头的gitee主页
⏩ 文章专栏:《C++指南》
期待您的关注
目录
1. 引言
2. 模板的基本概念
3. 函数模板
3.1 定义和语法
3.2 函数模板实例化
3.3 隐式实例化
3.4 显式实例化
3.5 模板函数的匹配规则
4. 类模板
4.1 定义和语法
4.2 成员函数的定义
4.3 模板参数的默认值
5. 模板的高级用法
5.1 模板特化
5.2 模板模板参数
6. 实战案例
6.1 函数模板示例
6.2 类模板示例
7. 常见问题与注意事项
8. 结语
9. 参考文献
1. 引言
C++模板是一种强大的泛型编程工具,它允许程序员编写独立于具体类型的代码。
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
通过模板,我们可以创建通用的函数和类,这些函数和类可以适用于多种数据类型,从而提高代码的复用性和灵活性。
本文将详细介绍C++模板的基本概念和使用方法。
2. 模板的基本概念
模板是C++的一种特性,用于创建函数或类的通用形式,这些形式可以应用于多种数据类型。
模板允许开发者编写一次代码,然后用不同的类型实例化,从而生成具体的函数或类。
3. 函数模板
3.1 定义和语法
函数模板的定义使用关键字template
,后跟模板参数列表,然后是函数声明或定义。模板参数列表通常包含一个或多个类型参数。
template
返回值类型 函数名(参数列表){}
template
T max(T a, T b) {
return (a > b) ? a : b;
}
注意:
typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
3.2 函数模板实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。
模板参数实例化分为:隐式实例化 和显式实例化。
3.3 隐式实例化
当调用函数模板时,编译器会根据传递的参数类型自动推导出模板参数的具体类型。
这种实例化,称为隐式实例化,如下方示例:
#include
template
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
template
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(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;
}
3.5 模板函数的匹配规则
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 与非模板函数匹配,编译器不需要特化
Add(1, 2); // 调用编译器特化的Add版本
}
4. 类模板
4.1 定义和语法
类模板的定义类似于函数模板,使用关键字template
,后跟模板参数列表,然后是类的定义。
template
class 类模板名
{
// 类内成员定义
};
示例代码:
template
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
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
void Stack::push(const T& item) {
if (top < size - 1) {
items[++top] = item;
} else {
std::cout << "Stack overflow" << std::endl;
}
}
template
T Stack::pop() {
if (top >= 0) {
return items[top--];
} else {
std::cout << "Stack underflow" << std::endl;
return T(); // 返回默认构造的对象
}
}
template
bool Stack::isEmpty() const {
return top == -1;
}
4.3 模板参数的默认值
模板参数可以有默认值,这样在实例化时可以省略某些参数。
template
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
或 Stack
),该实例的 Size
值就是固定的,不能在运行时改变
5. 模板的高级用法
5.1 模板特化
模板特化允许为特定的类型提供特殊的实现。
// 全特化
template <>
class Stack {
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
class Stack {
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 class Container, typename T, int Size>
class MyClass {
Container container;
public:
void add(const T& item) {
container.push(item);
}
T remove() {
return container.pop();
}
};
6. 实战案例
6.1 函数模板示例
编写一个通用的数组排序函数。
#include
template
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
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
void LinkedList::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
void LinkedList::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
bool LinkedList::contains(T value) const {
Node* current = head;
while (current) {
if (current->data == value) {
return true;
}
current = current->next;
}
return false;
}
template
void LinkedList::clear() {
Node* current = head;
while (current) {
Node* next = current->next;
delete current;
current = next;
}
head = nullptr;
}
int main() {
LinkedList 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++模板的过程中取得更大的进步!