c++ std::shared_ptr循环引用怎么解决 c++ weak_ptr用法【核心】

shared_ptr循环引用的本质是双方互相持有导致引用计数无法归零,典型场景包括双向链表、父子对象和观察者模式;weak_ptr通过不增加引用计数并配合lock()安全访问来打破循环。

shared_ptr循环引用的本质和典型场景

当两个对象各自用shared_ptr持有对方时,引用计数永远无法归零,导致内存泄漏。最常见于双向链表节点、父子对象(如树结构中父指针和子指针互相持有时)、观察者模式中观察者与被观察者互相强引用。

weak_ptr是打破循环的关键工具

weak_ptr不增加引用计数,只“观察”所指向对象是否还存在。它不能直接访问对象,必须通过lock()升级为shared_ptr——成功则说明对象仍存活,失败(返回空)则说明已被释放。

  • 把“非拥有关系”的那一端改为weak_ptr,例如:子节点存父节点用weak_ptr,父节点存子节点用shared_ptr
  • 访问前务必调用lock()并检查返回值,避免解引用已释放对象
  • weak_ptr本身线程安全(拷贝/赋值/lock不需额外同步),但其指向的对象生命周期仍需逻辑保证

实际写法示例(父子关系)

假设Parent持有children的shared_ptr,Child需反向访问Parent但不延长其生命周期:

struct Parent;
struct Child {
    std::weak_ptr parent; // 不参与所有权
    void doSomething() {
        auto p = parent.lock(); // 尝试获取临时shared_ptr
        if (p) {
            p->doWork(); // 安全调用
        } else {
            // Parent已被销毁,做清理或跳过
        }
    }
};

struct Parent {
    std::vector> children;
    void addChild() {
        auto c = std::make_shared();
        c->parent = shared_from_this(); // weak_ptr自动绑定当前shared_ptr
        children.push_back(c);
    }
};

其他辅助手段与注意事项

除了weak_ptr,还可结合设计层面规避:

  • 用原始指针(raw pointer)替代weak_ptr,前提是能严格保证生命周期——父对象一定比子对象活得久
  • 显式断开引用:在析构或重置前手动将shared_ptr置为空,尤其在容器或回调中
  • 避免在lambda捕获中隐式产生循环:[self = shared_from_this()]会延长自身生命周期,若self又持有该lambda,则成环;此时应捕获weak_ptr并在lambda内lock

weak_ptr不是万能胶,核心在于厘清谁是真正的所有者,让非所有者“弱观察”。用对了,循环引用就自然解开。