MST

星途 面试题库

面试题:C++ std::weak_ptr在复杂数据结构中的循环引用处理

考虑一个包含树形结构的复杂数据结构,树节点之间存在可能的循环引用(例如,父节点和子节点相互持有引用)。如何运用std::weak_ptr来解决这个循环引用问题,同时确保数据结构的遍历和操作不受影响?详细描述设计思路并给出关键代码片段。
49.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 使用std::weak_ptr打破循环引用:在存在循环引用的地方,将其中一个方向的引用改为std::weak_ptr。通常将子节点对父节点的引用改为std::weak_ptr,因为在遍历树时,从父节点到子节点的遍历更常见,这样可以保持从父节点到子节点的强引用关系,保证正常的遍历操作。
  2. 获取有效对象:在需要通过std::weak_ptr访问对象时,使用lock()方法检查对象是否仍然存在(即对应的std::shared_ptr是否还有引用)。如果存在,则返回一个std::shared_ptr,可以安全地访问对象。
  3. 遍历和操作不受影响:对于树的遍历,如深度优先搜索(DFS)或广度优先搜索(BFS),由于父节点到子节点的引用仍然是强引用,所以遍历逻辑与普通树类似。对于需要通过子节点访问父节点的操作,先通过std::weak_ptr获取父节点的std::shared_ptr,再进行操作。

关键代码片段

#include <memory>
#include <iostream>
#include <vector>

// 树节点类
class TreeNode {
public:
    int value;
    std::shared_ptr<TreeNode> parent;
    std::vector<std::shared_ptr<TreeNode>> children;
    std::weak_ptr<TreeNode> weakParent; // 使用std::weak_ptr打破循环引用

    TreeNode(int val) : value(val) {}

    // 添加子节点
    void addChild(std::shared_ptr<TreeNode> child) {
        children.push_back(child);
        child->weakParent = shared_from_this(); // 设置子节点的弱引用父节点
    }

    // 通过子节点获取父节点
    std::shared_ptr<TreeNode> getParent() {
        return weakParent.lock();
    }
};

// 深度优先搜索遍历树
void dfs(std::shared_ptr<TreeNode> node) {
    if (!node) return;
    std::cout << "Visiting node: " << node->value << std::endl;
    for (const auto& child : node->children) {
        dfs(child);
    }
}

可以通过以下方式使用这些代码:

int main() {
    std::shared_ptr<TreeNode> root = std::make_shared<TreeNode>(1);
    std::shared_ptr<TreeNode> child1 = std::make_shared<TreeNode>(2);
    std::shared_ptr<TreeNode> child2 = std::make_shared<TreeNode>(3);

    root->addChild(child1);
    root->addChild(child2);

    dfs(root);

    // 通过子节点获取父节点
    if (auto parent = child1->getParent()) {
        std::cout << "Child 1's parent value: " << parent->value << std::endl;
    }

    return 0;
}

在上述代码中,TreeNode类使用std::weak_ptr来表示子节点对父节点的引用,从而打破了可能存在的循环引用。dfs函数展示了如何在这种数据结构上进行正常的遍历,getParent函数展示了如何通过std::weak_ptr获取父节点。