- 设计方案
#include <memory>
class Node;
using NodePtr = std::shared_ptr<Node>;
using WeakNodePtr = std::weak_ptr<Node>;
class Node {
public:
int data;
NodePtr next;
WeakNodePtr prev;
Node(int value) : data(value) {}
};
void addNode(NodePtr& head, int value) {
NodePtr newNode = std::make_shared<Node>(value);
if (!head) {
head = newNode;
} else {
NodePtr current = head;
while (current->next) {
current = current->next;
}
current->next = newNode;
newNode->prev = current;
}
}
void deleteNode(NodePtr& head, int value) {
NodePtr current = head;
NodePtr prevNode;
while (current && current->data != value) {
prevNode = current;
current = current->next;
}
if (current) {
if (prevNode) {
prevNode->next = current->next;
if (current->next) {
current->next->prev = prevNode;
}
} else {
head = current->next;
if (head) {
head->prev.reset();
}
}
// 由于没有其他强引用,current会自动释放其内存
}
}
void traverse(NodePtr head) {
NodePtr current = head;
while (current) {
std::cout << current->data << " ";
current = current->next;
}
std::cout << std::endl;
}
- 智能指针类型选择原因
- std::shared_ptr:
- 用于表示对节点的“拥有权”。在添加节点时,使用
std::make_shared
创建新节点,这样能更高效地管理内存。在双向链表中,next
指针使用std::shared_ptr
,因为它需要保持对下一个节点的强引用,以确保下一个节点在使用过程中不会被意外释放。例如在遍历链表时,current = current->next
,如果next
不是强引用,下一个节点可能在遍历到之前就被释放了。
- std::weak_ptr:
prev
指针使用std::weak_ptr
,这是为了避免循环引用。在双向链表中,如果prev
也使用std::shared_ptr
,当两个节点互相指向对方时(例如节点A的next
指向节点B,节点B的prev
指向节点A),会形成循环引用,导致内存泄漏。std::weak_ptr
不增加引用计数,它允许观察std::shared_ptr
所管理的对象,但不会阻止该对象被释放。例如在删除节点操作中,当删除一个节点时,其前驱节点的next
指针和后继节点的prev
指针的调整,prev
指针作为std::weak_ptr
不会干扰节点内存的释放。