双向链表结构实现
#include <memory>
#include <iostream>
// 双向链表节点结构
struct Node {
int data;
std::unique_ptr<Node> next;
Node* prev;
Node(int value) : data(value), next(nullptr), prev(nullptr) {}
};
// 双向链表类
class DoublyLinkedList {
private:
std::unique_ptr<Node> head;
Node* tail;
public:
DoublyLinkedList() : head(nullptr), tail(nullptr) {}
// 插入新节点到链表头部
void insertAtHead(int value) {
auto newNode = std::make_unique<Node>(value);
if (head) {
head->prev = newNode.get();
newNode->next = std::move(head);
} else {
tail = newNode.get();
}
head = std::move(newNode);
}
// 插入新节点到链表尾部
void insertAtTail(int value) {
auto newNode = std::make_unique<Node>(value);
if (tail) {
tail->next = std::move(newNode);
newNode->prev = tail;
tail = newNode.get();
} else {
head = std::move(newNode);
tail = head.get();
}
}
// 删除指定值的节点
void deleteNode(int value) {
Node* current = head.get();
while (current) {
if (current->data == value) {
if (current == head.get()) {
head = std::move(current->next);
if (head) {
head->prev = nullptr;
} else {
tail = nullptr;
}
} else if (current == tail) {
tail = current->prev;
tail->next.reset();
} else {
current->prev->next = std::move(current->next);
current->next->prev = current->prev;
}
return;
}
current = current->next.get();
}
}
// 打印链表内容
void printList() {
Node* current = head.get();
while (current) {
std::cout << current->data << " ";
current = current->next.get();
}
std::cout << std::endl;
}
};
std::unique_ptr
相比于原始指针的优势
- 自动内存管理:
std::unique_ptr
在其生命周期结束时会自动释放所管理的内存,避免了手动释放内存可能导致的内存泄漏问题。在双向链表这种复杂数据结构中,节点的插入和删除操作频繁,手动管理内存容易出错,std::unique_ptr
大大简化了内存管理流程。
- 所有权语义清晰:
std::unique_ptr
明确表示对其所管理对象的唯一所有权,避免了多个指针指向同一对象导致的悬空指针和双重释放问题。在双向链表中,节点的移动(如插入和删除操作)涉及指针的转移,std::unique_ptr
的所有权语义使得这种操作更加安全和可预测。
可能面临的挑战及应对方法
- 跨函数传递的复杂性:由于
std::unique_ptr
的唯一性,在函数间传递时需要使用std::move
语义,这可能增加代码的复杂性。在双向链表操作中,例如在插入或删除节点时,需要确保std::unique_ptr
的正确转移。应对方法是在编写函数时明确参数和返回值的所有权转移方式,合理使用std::move
。
- 与遗留代码的兼容性:如果项目中部分代码仍然使用原始指针,与
std::unique_ptr
混合使用可能会带来问题。例如,一些C风格的库函数可能要求原始指针作为参数。应对方法是在接口处进行适当的转换,将std::unique_ptr
转换为原始指针传递给需要的函数,并确保在函数返回后std::unique_ptr
仍然能正确管理内存。同时,逐步将遗留代码迁移到使用智能指针。