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

C++ shared_ptr处理循环与非循环列表及weak_ptr使用疑问

智能指针循环引用场景的正确实现方案

核心规则

解决循环引用的核心是明确所有权边界:仅用shared_ptr标记「所有权持有」关系,非所有权的反向引用统一使用weak_ptr,避免双向强引用形成计数闭环。

循环列表问题修复

错误尝试原因说明

  • 全部next改为weak_ptr:链表的所有权逻辑是前节点持有后节点的所有权,全改为弱引用后没有强引用持有节点对象,节点创建后会立即被释放,因此出现过早释放的问题。
  • 仅将外部head改为weak_ptr:内部节点之间的循环强引用仍然存在,三个节点的强引用计数始终为1,永远无法触发销毁逻辑,因此内存泄漏。

正确实现方案(二选一即可)

方案1:仅循环位使用弱引用

保持链表「前节点持有后节点强引用」的逻辑,仅将尾节点指向头节点的循环引用改为weak_ptr,同时外部持有头节点的shared_ptr作为整个链表的所有者。如果需要兼容普通链表和循环链表的通用场景,更推荐使用方案2。

方案2:封装链表类主动解环

所有节点next保持shared_ptr,封装链表容器类,类内持有头节点的强引用,析构时主动将尾节点的next置空打破循环,即可触发所有节点的正常析构:

struct Elem {
    string cargo;
    shared_ptr<Elem> next;
    Elem(string cargo) {
        this->cargo = cargo;
    }
    ~Elem() {
        cout << "delete Elem " << cargo << endl;
    }
};

class CyclicList {
public:
    shared_ptr<Elem> head;
    shared_ptr<Elem> tail;

    ~CyclicList() {
        if (tail) tail->next.reset(); // 主动打破循环
    }

    static CyclicList make3() {
        CyclicList list;
        list.head = shared_ptr<Elem>(new Elem("A"));
        auto B = shared_ptr<Elem>(new Elem("B"));
        auto C = shared_ptr<Elem>(new Elem("C"));
        list.head->next = B;
        B->next = C;
        C->next = list.head;
        list.tail = C;
        return list;
    }
};

static void test3() {
    CyclicList list = CyclicList::make3();
    auto head = list.head->next;
    cout << "elem A should not be deleted yet" << endl;
    // list离开作用域时析构自动解环,所有节点正常释放
}

带父指针的树结构修复

树的所有权逻辑是父节点持有子节点的所有权,子节点指向父节点属于非所有权的反向引用,因此将parent字段改为weak_ptr即可解决循环引用问题:

struct Node {
    weak_ptr<Node> parent; // 仅此处改为weak_ptr
    Node(shared_ptr<Node> parent) {
        this->parent = parent;
        if (parent!=nullptr)
            depth = parent->depth+1;
    }
    int depth = 0;
    vector<shared_ptr<Node>> children;
    shared_ptr<Node> addChild(shared_ptr<Node> parent) {
        shared_ptr<Node> child(new Node(parent));
        children.push_back(child);
        return child;
    }
};

使用父指针时调用parent.lock()即可转为可用的shared_ptr,如果父节点已经被销毁,lock()会返回空指针,可自行判断处理。

weak_ptr计数原理解答

你之前的认知偏差是混淆了对象本身和控制块的生命周期:

  • shared_ptr和weak_ptr共同指向同一个独立的控制块(即你提到的计数辅助对象),控制块中存储强引用计数、弱引用计数、删除器等信息。
  • 当强引用计数归零时,对象本身会被立即销毁,此时弱引用计数可能还不为零,控制块会继续存在,直到所有weak_ptr都被释放、弱引用计数也归零后,控制块才会被销毁。
  • 不存在「控制块先释放、对象还存在」的可能,控制块的生命周期始终长于对象本身的生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 17:15:00