为何实现AABBTree时std::weak_ptr<T>::lock返回空shared_ptr?
问题根因
你遇到的lock()返回空的核心原因是刚创建的子节点对象在AddChild函数执行结束后就被立即销毁了:
- 你在
AddChild函数中用std::make_shared创建的子节点强引用child是函数局部变量,函数执行完后这个局部变量就会被销毁 - 你的子节点容器
m_Children存储的是std::weak_ptr类型,属于弱引用,不会增加对象的引用计数 - 此时子节点的引用计数会从1降到0,对应的内存被直接释放,后续你通过
weak_ptr::lock()自然无法获取到有效对象
设计修正建议
你的循环引用规避方案搞反了所有权关系,这才是导致问题的本质原因:
正常树结构的所有权归父节点,为了避免父子互相持有强引用导致的内存泄漏,正确的成员设计应该是:
- 父节点持有子节点的强引用:将子节点容器改为
std::vector<std::shared_ptr<AABBTreeNode>>,保证父节点存活时子节点不会被意外释放 - 子节点持有父节点的弱引用:将父节点成员改为
std::weak_ptr<AABBTreeNode>,子节点需要访问父节点时再调用lock()获取临时强引用即可
调整后的代码改动如下:
1. 调整成员变量类型
class AABBTreeNode { // 其他成员省略 private: std::weak_ptr<AABBTreeNode> m_Parent; // 父节点存弱引用 std::vector<std::shared_ptr<AABBTreeNode>> m_Children; // 子节点存强引用 };
2. 构造函数适配
构造函数接收父节点的std::shared_ptr后,赋值给m_Parent这个弱引用成员即可。
3. 业务代码调整
AddChild函数不需要改动核心逻辑,直接把创建的子节点强引用存入容器即可:
void AABBTreeNode::AddChild(std::shared_ptr<open3d::geometry::AxisAlignedBoundingBox> partitionBound) { auto child = std::make_shared<AABBTreeNode>(shared_from_this(), m_Mesh, partitionBound); m_Children.push_back(child); }
AddTriangles函数不需要再调用lock(),直接使用容器里的强引用即可:
void AABBTreeNode::AddTriangles(std::vector<Eigen::Vector3d>& centers) { auto center = m_Bbox->GetCenter(); for (auto& boundPoint : m_Bbox->GetBoxPoints()) { // 此处为计算最多8个子节点的minBound和maxBound的代码 auto adjustedPartitionBound = std::make_shared<open3d::geometry::AxisAlignedBoundingBox>(minBound, maxBound); AddChild(adjustedPartitionBound); auto child = m_Children.back(); // 不需要判空,只要make_shared成功就一定有效 child->AddTriangles(partitionCenters); } }
这个设计既规避了循环引用问题,也不会出现子节点被意外释放的情况。
内容的提问来源于stack exchange,提问作者Roland Deschain
相关产品推荐
相关产品推荐

