C++ 模板(泛型编程)
C++ 模板是实现泛型编程的核心机制,通过将数据类型参数化,让一段代码适配多种数据类型,避免重复编写同逻辑不同类型的代码,大幅提升代码复用性。
函数模板
核心概念
- 目的 :将函数的参数类型、返回值类型 "参数化",让同一个函数逻辑处理不同数据类型(如加法函数同时支持
int、double、std::string)。 - 本质:模板本身不是可执行代码,编译器在调用模板时,会根据传入的具体类型 "实例化" 出对应函数代码(模板实例化),最终运行的是实例化后的具体函数。
- 关键术语 :
- 模板声明:
template <typename T>或template <class T>(两者等价,typename更直观),声明 "类型占位符"; - 模板参数:
T(可自定义名称,如Type、T1)是类型占位符,代表任意数据类型,同一模板中参数名必须唯一; - 模板函数:模板实例化后生成的具体函数(如
add<int>是针对int类型的模板函数)。
- 模板声明:
基本语法
模板定义
cpp
/**
* 通用加法函数模板
* @brief 实现任意支持"+"运算符类型的加法操作
* @tparam T 类型占位符,代表任意支持"+"运算符的类型(如int、double、std::string)
* @param a 第一个加数,类型为T
* @param b 第二个加数,类型为T(自动推导时需与a类型严格一致)
* @return T 两个数相加的结果,类型与参数一致
* @note 模板仅声明类型规则,编译器调用时才生成具体类型的函数代码
*/
template <typename T>
T add(T a, T b) {
return a + b;
}
调用方式
| 调用方式 | 示例 | 详细说明 |
|---|---|---|
| 自动类型推导 | add(10, 20); |
编译器根据实参类型(int)推导T=int,生成int add(int, int)并调用 |
| 显式指定类型 | add<double>(3.14, 2.5); |
强制生成double add(double, double),无视实参的自动推导结果 |
| 空模板参数列表 | add<>(10, 20); |
强制调用模板版本(即使存在同名普通函数),编译器仍推导T=int |
显式指定类型规则
- 精确匹配优先:显式指定的类型与模板参数完全一致的版本优先选择;
- 特化模板优先:针对指定类型的特化模板(全特化 / 偏特化)优先于通用模板;
- 参数数量匹配:显式指定的类型数量需与模板参数数量一致(除非模板有默认参数);
- 类型转换限制:显式指定类型后,实参允许隐式转换为指定类型(如
add<double>(10, 3.14)中int转double);自动推导时不支持隐式转换; - 重载决议:按 "最具体" 原则选择模板(SFINAE 原则:替换失败不是错误,仅跳过不匹配的模板)。
函数模板与普通函数的区别
| 对比项 | 普通函数 | 函数模板 |
|---|---|---|
| 代码生成时机 | 编译阶段直接生成对应代码 | 调用时根据实参 / 指定类型实例化生成代码 |
| 类型支持 | 仅支持定义时的固定类型(如int add(int, int)仅处理int) |
支持任意满足操作要求的类型(如add需类型支持+运算符) |
| 隐式类型转换 | 支持(如add(3, 4.5)中4.5隐式转int) |
自动推导:不支持(实参类型需严格匹配); 显式指定:允许实参隐式转指定类型 |
| 性能 | 无额外生成开销,仅一份代码 | 不同类型生成不同代码,可能导致 "代码膨胀"(现代编译器会优化重复逻辑) |
| 匹配优先级 | 同名场景下优先级最高 | 低于普通函数,高于模板特化版本 |
模板参数规则
多参数模板
一个模板可声明多个类型参数,适配不同类型的参数组合:
cpp
/**
* 通用乘法函数模板(多参数)
* @brief 实现两种不同类型数据的乘法操作
* @tparam T1 第一个乘数的类型占位符
* @tparam T2 第二个乘数的类型占位符
* @param a 第一个乘数,类型为T1
* @param b 第二个乘数,类型为T2
* @return auto 返回值类型由`a*b`的结果类型推导(C++14起支持;C++11需写:auto multiply(T1 a, T2 b) -> decltype(a*b))
* @note 多参数模板的类型可独立推导,无需一致
*/
template <typename T1, typename T2>
auto multiply(T1 a, T2 b) {
return a * b;
}
// 调用示例:T1=int、T2=double,返回值为double(int*double的结果类型)
multiply(3, 4.5);
默认模板参数
可为模板参数指定默认类型,调用时未指定 / 推导类型则使用默认值:
cpp
/**
* 通用自增函数模板(带默认参数)
* @brief 实现任意类型的自增操作(+1)
* @tparam T 类型占位符,默认类型为int
* @param value 待自增的值,类型为T
* @return T 自增后的结果,类型与参数一致
* @note 空模板列表<>可覆盖默认类型
*/
template <typename T = int>
T increment(T value) {
return value + 1;
}
// 调用示例
increment(5); // 未指定类型,使用默认T=int,返回6
increment<>(5.0); // 空模板列表强制推导,T=double,返回6.0
increment<double>(5); // 显式指定T=double,5隐式转为double,返回6.0
模板重载与优先级
模板重载指定义多个同名模板(或普通函数),编译器按以下优先级选择:
- 普通函数 > 模板特化版本 > 通用模板版本;
- 更 "具体" 的模板优先(如
T*模板优先于T模板); - 空模板列表
<>可强制调用模板版本(跳过普通函数)。
cpp
#include <iostream>
using namespace std;
/**
* 普通打印函数(int专用)
* @brief 处理int类型的打印,优先级最高
* @param x 待打印的int值
* @return void
*/
void print(int x) {
cout << "普通函数: " << x << endl;
}
/**
* 通用打印模板函数
* @brief 处理任意类型的打印,优先级低于普通函数和特化模板
* @tparam T 类型占位符
* @param x 待打印的值,类型为T
* @return void
*/
template <typename T>
void print(T x) {
cout << "通用模板函数: " << x << endl;
}
/**
* 打印模板的int特化版本
* @brief 针对int类型的优化打印,优先级高于通用模板、低于普通函数
* @param x 待打印的int值
* @return void
* @note 函数模板仅支持全特化,不支持偏特化
*/
template <>
void print<int>(int x) {
cout << "int特化模板函数: " << x << endl;
}
// 调用测试
int main() {
print(10); // 匹配普通函数 → 输出:普通函数: 10
print<>(10); // 强制调用模板 → 匹配int特化版本 → 输出:int特化模板函数: 10
print(3.14); // 无普通函数匹配 → 调用通用模板 → 输出:通用模板函数: 3.14
return 0;
}
模板特例化
为特定类型提供定制化实现(优化逻辑 / 适配特殊类型),分为全特化(所有参数指定具体类型)和偏特化(仅指定部分参数,仅类模板支持)。
函数模板全特化
cpp
#include <iostream>
#include <string>
using namespace std;
/**
* 通用打印模板函数
* @tparam T 任意类型占位符
* @param s 待打印的值
* @return void
*/
template <typename T>
void print(T s) {
cout << "通用模板: " << s << endl;
}
/**
* 打印模板的std::string全特化版本
* @brief 为字符串类型定制打印逻辑
* @param s 待打印的字符串
* @return void
* @note 特化模板需与通用模板的函数签名完全匹配
*/
template <>
void print<std::string>(std::string s) {
cout << "字符串特化: " << s << endl;
}
int main() {
print(std::string("hello")); // 显式转string,匹配特化版本 → 输出:字符串特化: hello
print(100); // 匹配通用模板 → 输出:通用模板: 100
return 0;
}
类模板偏特化(函数模板不支持)
为模板的部分参数 / 类型修饰(如指针、引用)定制实现:
cpp
/**
* 通用容器类模板
* @tparam T 容器存储的类型
*/
template <typename T>
class Box {
public:
T content;
void show() { cout << "通用Box: " << content << endl; }
};
/**
* Box类模板的指针类型偏特化
* @brief 为存储指针的Box定制实现(解引用打印)
* @tparam T 指针指向的类型
*/
template <typename T>
class Box<T*> {
public:
T* content;
void show() { cout << "指针Box: " << *content << endl; } // 解引用打印
};
// 调用示例
int main() {
Box<int> intBox;
intBox.content = 42;
intBox.show(); // 输出:通用Box: 42
int num = 100;
Box<int*> ptrBox;
ptrBox.content = #
ptrBox.show(); // 输出:指针Box: 100
return 0;
}
类模板
类模板将类的成员变量、成员函数类型参数化,实例化后生成适配不同类型的 "模板类"。
类模板定义与使用
cpp
#include <iostream>
#include <string>
using namespace std;
/**
* 通用容器类模板Box
* @brief 存储任意类型的单个数据,提供设置/获取接口
* @tparam T 容器存储的数据类型
*/
template <class T>
class Box {
private:
T content; // 成员变量类型参数化
public:
/**
* 设置容器内容
* @param val 要存储的值,类型为T
* @return void
*/
void set(T val) {
content = val;
}
/**
* 获取容器内容
* @return T 存储的内容,类型为T
*/
T get() {
return content;
}
};
// 类模板使用:必须显式指定模板参数(无法自动推导)
int main() {
Box<int> intBox; // 实例化int类型的Box类(模板类)
intBox.set(42);
cout << intBox.get() << endl; // 输出42
Box<string> strBox; // 实例化string类型的Box类
strBox.set("hello");
cout << strBox.get() << endl; // 输出hello
return 0;
}
类模板作为函数参数
指定具体类型(最局限)
仅接受某一具体实例化的模板类:
cpp
/**
* 打印int类型Box的内容
* @brief 仅处理Box<int>类型的对象
* @param box Box<int>类型的对象引用
* @return void
*/
void printBox(Box<int>& box) {
cout << box.get() << endl;
}
// 调用:仅能传入Box<int>
Box<int> ib;
ib.set(10);
printBox(ib); // 正确
// Box<double> db; printBox(db); // 错误:类型不匹配
参数模板化(适配任意 Box 实例)
函数模板接收任意类型的 Box 类:
cpp
/**
* 打印任意类型Box的内容
* @brief 处理Box<T>类型的任意实例
* @tparam T Box的模板参数类型
* @param box Box<T>类型的对象引用
* @return void
*/
template <class T>
void printBoxTemplate(Box<T>& box) {
cout << box.get() << endl;
}
// 调用:支持任意Box实例
Box<int> ib; ib.set(10);
Box<double> db; db.set(3.14);
printBoxTemplate(ib); // 正确
printBoxTemplate(db); // 正确
整个类模板化(适配任意模板类)
函数模板接收任意带get()方法的模板类:
cpp
/**
* 打印任意带get()方法的模板类内容
* @brief 处理任意有get()成员函数的类对象
* @tparam T 任意类类型(需有get()方法)
* @param boxObj T类型的对象引用
* @return void
*/
template <class T>
void printBoxFull(T& boxObj) {
cout << boxObj.get() << endl;
}
// 调用:支持Box、自定义模板类等
Box<string> sb; sb.set("test");
printBoxFull(sb); // 正确
// 自定义模板类也可适配
template <typename U>
class Bag {
public:
U val;
U get() { return val; }
};
Bag<double> bg; bg.val = 5.5;
printBoxFull(bg); // 正确
类模板的静态成员
类模板的静态成员属于实例化后的每个模板类,不同类型的模板类拥有独立的静态成员,互不共享:
cpp
/**
* 带静态成员的类模板
* @brief 演示类模板静态成员的独立性
* @tparam T 任意类型
*/
template <class T>
class MyClass {
public:
static T count; // 静态成员,类型参数化
};
/**
* 类模板静态成员的类外初始化
* @brief 必须通过模板参数限定,为每个实例化类初始化静态成员
*/
template <class T>
T MyClass<T>::count = 0; // 初始值为0
// 使用示例
int main() {
MyClass<int>::count++; // int版本count → 1
MyClass<double>::count++; // double版本count → 1
MyClass<int>::count++; // int版本count → 2
cout << MyClass<int>::count << " " << MyClass<double>::count << endl; // 输出:2 1
return 0;
}
关键概念对比
| 概念 | 详细说明 |
|---|---|
| 函数模板 vs 类模板 | 函数模板:参数化函数类型,实例化生成具体函数; 类模板:参数化类类型,实例化生成具体类 |
| 模板函数 | 函数模板实例化后的具体函数(如add<int>) |
| 模板类 | 类模板实例化后的具体类(如Box<int>) |
| 类模板静态成员 | 每个模板类(如MyClass<int>/MyClass<double>)拥有独立静态成员,互不共享 |
| 全特化 vs 偏特化 | 全特化:为所有模板参数指定具体类型(函数 / 类模板均支持); 偏特化:仅指定部分参数(仅类模板支持) |
模板核心机制与注意事项
两次编译规则
模板编译分两个阶段,确保语法和类型双重检查:
- 第一阶段(模板定义阶段):检查语法正确性(如括号、分号),不检查类型相关逻辑(如
a + b的+是否支持); - 第二阶段(实例化阶段):生成具体类型代码时,检查该类型是否支持模板中的操作(如自定义类未重载
+则编译失败)。
类型参数限制
-
自动推导时:模板参数需与实参类型严格匹配,不支持隐式转换;显式指定类型时,实参可隐式转换为指定类型:
cppadd(10, 3.14); // 错误:int和double推导T冲突 add<double>(10, 3.14); // 正确:10隐式转double -
操作限制:模板中的操作(如
+、成员调用)需适配实例化类型,否则编译失败:cppstruct MyStruct {}; add(MyStruct{}, MyStruct{}); // 错误:MyStruct未重载+运算符
代码膨胀问题
不同类型的模板实例会生成独立代码,可能导致可执行文件体积变大。优化方式:
- 复用相同类型的模板实例;
- 依赖编译器对重复逻辑的合并优化;
- 对高频类型提前实例化模板。
模板声明与定义位置
模板的声明和定义通常需放在同一文件(如头文件)------ 实例化时需要完整的模板定义(确保模板定义、实例化请求在同一个编译单元 ),若定义放在.cpp文件,编译器可能找不到定义导致链接错误。
示例:链表模板(头文件实现)
链表是经典的通用容器,适合用类模板实现(支持任意类型存储)。以下是完整的单向链表模板,包含核心操作 + 详细注释,符合模板 "声明 + 定义放头文件" 的最佳实践:
头文件 MyLink.h
cpp
#ifndef MY_LINK_H
#define MY_LINK_H
#include <iostream>
#include <stdexcept> // 异常处理
#include <string> // 支持string类型示例
/**
* 通用单向链表类模板
* @brief 实现任意类型的单向链表,支持头插、尾插、删除、遍历、清空等核心操作
* @tparam T1 链表存储的元素类型(支持int、double、string等任意可拷贝/打印的类型)
*/
template <typename T1>
class MyLink {
private:
// 内部节点结构体重命名为Node,避免与外部类冲突
struct Node {
T1 data; // 节点存储的数据
Node* next; // 指向下一个节点的指针(核心修正:值类型→指针类型)
/**
* 节点构造函数
* @param val 节点初始化数据
* @param nxt 下一个节点的指针(默认nullptr)
*/
Node(const T1& val, Node* nxt = nullptr) : data(val), next(nxt) {}
};
Node* head; // 链表头节点指针(空链表时为nullptr)
size_t len; // 链表长度(避免遍历统计,提升效率)
public:
/**
* 链表构造函数
* @brief 初始化空链表(头节点=nullptr,长度=0)
*/
MyLink();
/**
* 链表析构函数
* @brief 释放所有节点内存,避免内存泄漏
*/
~MyLink();
/**
* 尾插法添加元素
* @brief 将元素添加到链表末尾
* @param val 要添加的元素(const引用避免拷贝开销)
* @return void
*/
void pushBack(const T1& val);
/**
* 头插法添加元素
* @brief 将元素添加到链表头部(效率O(1))
* @param val 要添加的元素
* @return void
*/
void pushFront(const T1& val);
/**
* 删除第一个匹配的元素
* @brief 找到第一个等于val的节点并释放内存
* @param val 要删除的元素值
* @return bool 删除成功返回true,元素不存在返回false
*/
bool remove(const T1& val);
/**
* 遍历打印链表
* @brief 从头部到尾部打印所有元素,空链表提示"空链表"
* @return void
*/
void print() const;
/**
* 获取链表长度
* @return size_t 链表节点个数(无符号整数)
*/
size_t size() const;
/**
* 清空链表
* @brief 释放所有节点内存,恢复为空链表
* @return void
*/
void clear();
/**
* 获取指定索引的元素
* @brief 支持随机访问(需遍历,效率O(n))
* @param idx 元素索引(从0开始)
* @return T1& 元素的引用(支持修改)
* @throw std::out_of_range 索引越界时抛出异常
*/
T1& at(size_t idx);
};
// ========== 类模板成员函数定义(必须在头文件中) ==========
template <typename T1>
MyLink<T1>::MyLink() : head(nullptr), len(0) {}
template <typename T1>
MyLink<T1>::~MyLink() {
clear(); // 复用clear()释放所有节点
}
template <typename T1>
void MyLink<T1>::pushBack(const T1& val) {
Node* newNode = new Node(val); // 创建新节点
if (head == nullptr) { // 空链表:新节点作为头节点
head = newNode;
} else { // 非空链表:遍历到尾部
Node* cur = head;
while (cur->next != nullptr) {
cur = cur->next;
}
cur->next = newNode;
}
len++; // 长度+1
}
template <typename T1>
void MyLink<T1>::pushFront(const T1& val) {
// 新节点的next指向原头节点,再更新头节点
head = new Node(val, head);
len++;
}
template <typename T1>
bool MyLink<T1>::remove(const T1& val) {
if (head == nullptr) return false; // 空链表直接返回
// 情况1:删除头节点
if (head->data == val) {
Node* temp = head;
head = head->next;
delete temp;
len--;
return true;
}
// 情况2:删除中间/尾部节点
Node* cur = head;
while (cur->next != nullptr && cur->next->data != val) {
cur = cur->next;
}
if (cur->next == nullptr) return false; // 未找到元素
// 找到匹配节点,释放内存
Node* temp = cur->next;
cur->next = cur->next->next;
delete temp;
len--;
return true;
}
template <typename T1>
void MyLink<T1>::print() const {
if (head == nullptr) {
std::cout << "链表为空" << std::endl;
return;
}
Node* cur = head;
std::cout << "链表元素: ";
while (cur != nullptr) {
std::cout << cur->data << " ";
cur = cur->next;
}
std::cout << std::endl;
}
template <typename T1>
size_t MyLink<T1>::size() const {
return len;
}
template <typename T1>
void MyLink<T1>::clear() {
Node* cur = head;
while (cur != nullptr) {
Node* temp = cur;
cur = cur->next;
delete temp; // 逐个释放节点
}
head = nullptr; // 恢复空链表状态
len = 0;
}
template <typename T1>
T1& MyLink<T1>::at(size_t idx) {
if (idx >= len) {
throw std::out_of_range("MyLink: 索引越界");
}
Node* cur = head;
for (size_t i = 0; i < idx; i++) {
cur = cur->next;
}
return cur->data;
}
#endif // MY_LINK_H
使用示例(main.cpp)
cpp
#include <iostream>
#include "MyLink.h"
#include <string>
int main() {
// ========== 示例1:int类型链表 ==========
MyLink<int> intLink;
intLink.pushBack(10); // 尾插10
intLink.pushBack(20); // 尾插20
intLink.pushFront(5); // 头插5
intLink.print(); // 输出:链表元素: 5 10 20
std::cout << "链表长度: " << intLink.size() << std::endl; // 输出:3
intLink.remove(10); // 删除10
intLink.print(); // 输出:链表元素: 5 20
std::cout << "索引0的元素: " << intLink.at(0) << std::endl; // 输出:5
// ========== 示例2:string类型链表 ==========
MyLink<std::string> strLink;
strLink.pushBack("hello");
strLink.pushBack("template");
strLink.print(); // 输出:链表元素: hello template
// ========== 清空链表 ==========
intLink.clear();
intLink.print(); // 输出:链表为空
return 0;
}
编译运行说明
-
编译命令(GCC/Clang):
bashg++ main.cpp -o MyLinkDemo ./MyLinkDemo -
输出结果:
链表元素: 5 10 20 链表长度: 3 链表元素: 5 20 索引0的元素: 5 链表元素: hello template 链表为空
核心注意事项(模板特性相关)
-
模板编译规则 :类模板的成员函数定义必须和声明放在头文件中(如上述
MyLink.h),若拆分到.cpp文件(不含main函数),需手动显式实例化(仅支持指定类型):MyLink.hpp
cpp#ifndef MY_LINK_H #define MY_LINK_H #include <iostream> #include <stdexcept> // 异常处理 #include <string> // 支持string类型示例 /** * 通用单向链表类模板 * @brief 实现任意类型的单向链表,支持头插、尾插、删除、遍历、清空等核心操作 * @tparam T1 链表存储的元素类型(支持int、double、string等任意可拷贝/打印的类型) */ template <typename T1> class MyLink { private: // 修正:内部节点结构体重命名为Node,避免与外部类冲突 struct Node { T1 data; // 节点存储的数据 Node* next; // 指向下一个节点的指针(核心修正:值类型→指针类型) /** * 节点构造函数 * @param val 节点初始化数据 * @param nxt 下一个节点的指针(默认nullptr) */ Node(const T1& val, Node* nxt = nullptr) : data(val), next(nxt) {} }; Node* head; // 链表头节点指针(空链表时为nullptr) size_t len; // 链表长度(避免遍历统计,提升效率) public: /** * 链表构造函数 * @brief 初始化空链表(头节点=nullptr,长度=0) */ MyLink(); /** * 链表析构函数 * @brief 释放所有节点内存,避免内存泄漏 */ ~MyLink(); /** * 尾插法添加元素 * @brief 将元素添加到链表末尾 * @param val 要添加的元素(const引用避免拷贝开销) * @return void */ void pushBack(const T1& val); /** * 头插法添加元素 * @brief 将元素添加到链表头部(效率O(1)) * @param val 要添加的元素 * @return void */ void pushFront(const T1& val); /** * 删除第一个匹配的元素 * @brief 找到第一个等于val的节点并释放内存 * @param val 要删除的元素值 * @return bool 删除成功返回true,元素不存在返回false */ bool remove(const T1& val); /** * 遍历打印链表 * @brief 从头部到尾部打印所有元素,空链表提示"空链表" * @return void */ void print() const; /** * 获取链表长度 * @return size_t 链表节点个数(无符号整数) */ size_t size() const; /** * 清空链表 * @brief 释放所有节点内存,恢复为空链表 * @return void */ void clear(); /** * 获取指定索引的元素 * @brief 支持随机访问(需遍历,效率O(n)) * @param idx 元素索引(从0开始) * @return T1& 元素的引用(支持修改) * @throw std::out_of_range 索引越界时抛出异常 */ T1& at(size_t idx); }; #endif // MY_LINK_HMyLink.cpp
cpp#include "MyLink.cpp" #include <iostream> #include <string> // 示例:在MyLink.cpp中显式实例化int和string类型 template class MyLink<int>; template class MyLink<std::string>; // ========== 类模板成员函数定义(必须在头文件中) ========== template <typename T1> MyLink<T1>::MyLink() : head(nullptr), len(0) {} template <typename T1> MyLink<T1>::~MyLink() { clear(); // 复用clear()释放所有节点 } template <typename T1> void MyLink<T1>::pushBack(const T1& val) { Node* newNode = new Node(val); // 创建新节点 if (head == nullptr) { // 空链表:新节点作为头节点 head = newNode; } else { // 非空链表:遍历到尾部 Node* cur = head; while (cur->next != nullptr) { cur = cur->next; } cur->next = newNode; } len++; // 长度+1 } template <typename T1> void MyLink<T1>::pushFront(const T1& val) { // 新节点的next指向原头节点,再更新头节点 head = new Node(val, head); len++; } template <typename T1> bool MyLink<T1>::remove(const T1& val) { if (head == nullptr) return false; // 空链表直接返回 // 情况1:删除头节点 if (head->data == val) { Node* temp = head; head = head->next; delete temp; len--; return true; } // 情况2:删除中间/尾部节点 Node* cur = head; while (cur->next != nullptr && cur->next->data != val) { cur = cur->next; } if (cur->next == nullptr) return false; // 未找到元素 // 找到匹配节点,释放内存 Node* temp = cur->next; cur->next = cur->next->next; delete temp; len--; return true; } template <typename T1> void MyLink<T1>::print() const { if (head == nullptr) { std::cout << "链表为空" << std::endl; return; } Node* cur = head; std::cout << "链表元素: "; while (cur != nullptr) { std::cout << cur->data << " "; cur = cur->next; } std::cout << std::endl; } template <typename T1> size_t MyLink<T1>::size() const { return len; } template <typename T1> void MyLink<T1>::clear() { Node* cur = head; while (cur != nullptr) { Node* temp = cur; cur = cur->next; delete temp; // 逐个释放节点 } head = nullptr; // 恢复空链表状态 len = 0; } template <typename T1> T1& MyLink<T1>::at(size_t idx) { if (idx >= len) { throw std::out_of_range("MyLink: 索引越界"); } Node* cur = head; for (size_t i = 0; i < idx; i++) { cur = cur->next; } return cur->data; }main.c
cpp#include "MyLink.cpp" int main() { // ========== 示例1:int类型链表 ========== MyLink<int> intLink; intLink.pushBack(10); // 尾插10 intLink.pushBack(20); // 尾插20 intLink.pushFront(5); // 头插5 intLink.print(); // 输出:链表元素: 5 10 20 std::cout << "链表长度: " << intLink.size() << std::endl; // 输出:3 intLink.remove(10); // 删除10 intLink.print(); // 输出:链表元素: 5 20 std::cout << "索引0的元素: " << intLink.at(0) << std::endl; // 输出:5 // ========== 示例2:string类型链表 ========== MyLink<std::string> strLink; strLink.pushBack("hello"); strLink.pushBack("template"); strLink.print(); // 输出:链表元素: hello template // ========== 清空链表 ========== intLink.clear(); intLink.print(); // 输出:链表为空 return 0; } -
类型适配性:模板支持任意类型,但需满足:
-
支持
==比较(remove()操作需要); -
支持
std::cout打印(print()操作需要); -
自定义类型需重载
==和<<运算符,例如:cppstruct Person { std::string name; int age; bool operator==(const Person& p) const { return name == p.name && age == p.age; } }; std::ostream& operator<<(std::ostream& os, const Person& p) { os << p.name << "(" << p.age << ")"; return os; } // 即可使用 MyLink<Person> personLink;
-
-
内存安全 :析构函数调用
clear()释放所有节点,避免内存泄漏;clear()中逐个删除节点并重置head和len,确保链表状态正确。
扩展方向
可基于此模板扩展双向链表、排序链表、迭代器支持等功能,核心模板语法不变,仅需补充节点结构(如双向链表加Node* prev)和对应成员函数。