You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用std::shared_ptr实现双向链表,删除节点需清空其指针吗?

C++双向链表删除节点时,是否需要置空节点的shared_ptr指针?

我正在用C++实现基于std::shared_ptr的双向链表,定义的Node和List结构体如下:

struct Node {
    std::shared_ptr<Node> prev, next;
    int data;

    Node(int data) : data(data), next(nullptr), prev(nullptr){};
    Node(int data, std::shared_ptr<Node> prev, std::shared_ptr<Node> next)
        : data(data), prev(prev), next(next){};

    ~Node(){};
};

struct List {
    std::shared_ptr<Node> head, tail;
    size_t size;
    
    // 其他方法...
    void remove(int data);

    List();
    List(std::initializer_list<int> list);
    List(size_t s, int data = 0);
    ~List();
};

现在在实现remove方法时,我纠结一个问题:删除目标节点时,要不要把该节点的prev和next指针置空?我写的示例代码片段如下:

// value - 要删除的目标数值
if (curr->data == value) {
    curr->prev->next = curr->next;
    curr->next->prev = curr->prev;

    std::shared_ptr<Node> tmp = curr;
    curr = curr->next;
    tmp->prev = nullptr;
    tmp->next = nullptr;
    tmp = nullptr;

    size--;
}

核心分析

先明确std::shared_ptr的核心特性:它基于引用计数管理内存,当指向对象的所有shared_ptr都被销毁/置空时,引用计数降为0,对象才会被自动释放。

1. 不置空指针的后果

当你把节点从链表中摘除(curr->prev->next = curr->next; curr->next->prev = curr->prev;)后,链表的其他节点已经不再持有指向该节点的shared_ptr,但被删除节点自身还持有prev和next的shared_ptr,这会让这两个相邻节点的引用计数多维持一个额外的计数,直到被删除节点被销毁。

不过当你把tmp置空后,指向被删除节点的shared_ptr只剩curr(但curr已经被重新赋值为curr->next),此时被删除节点的引用计数降为0,会被自动销毁,它持有的prev和next也会被析构,相邻节点的引用计数会随之减少,不会造成内存泄漏。

2. 置空指针的意义

置空tmp->prev和tmp->next是可选但推荐的操作:

  • 可以让相邻节点的引用计数立即减少,而不是等到被删除节点销毁时才触发;
  • 避免被删除节点在销毁前,意外通过prev/next访问链表的其他节点(虽然你的代码中已经没有这种访问可能,但这是一种良好的编码习惯)。

3. 更关键的问题:循环引用

你当前的Node结构存在致命的循环引用问题:节点之间用shared_ptr互相指向prev和next,比如节点A的next指向节点B,节点B的prev指向节点A,此时两个节点的引用计数至少为1(互相持有),即使链表的head/tail被置空,这两个节点也永远不会被释放,导致内存泄漏。

解决这个问题的正确做法是:把Node中的prev改成std::weak_ptr<Node>,因为prev属于反向引用,用弱指针不会增加引用计数,从而避免循环引用。修改后的Node结构如下:

struct Node {
    std::weak_ptr<Node> prev;
    std::shared_ptr<Node> next;
    int data;

    Node(int data) : data(data), next(nullptr){};
    Node(int data, std::weak_ptr<Node> prev, std::shared_ptr<Node> next)
        : data(data), prev(prev), next(next){};

    ~Node(){};
};

这样,当节点被从链表中摘除后,只要没有其他shared_ptr指向它,引用计数就会降为0,节点会被正确释放,同时不会因为反向引用导致循环。


内容的提问来源于stack exchange,提问作者lian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 23:55:13