【问题标题】:Using smart pointers for C++ binary search tree使用 C++ 二叉搜索树的智能指针
【发布时间】:2018-01-20 22:15:53
【问题描述】:

我在 C++ 中实现了一个二叉搜索树。我没有使用裸指针指向子节点,而是使用了std::shared_ptr。树的节点实现如下

struct treeNode;
typedef std::shared_ptr<treeNode> pTreeNode;

struct treeNode {
    T key;
    pTreeNode left;
    pTreeNode right;
    treeNode(T key) : key(key), left(nullptr), right(nullptr) {}
};

当从 BST 中删除一个节点时,其中一种情况是该节点只有一个子节点。节点被这个孩子简单地替换,如下所示:

             |        remove node                  
            node      --------->     |
               \                    right 
              right                

在类似的 Java 实现中,这可以编码为:

node = node.getRight();

在我的 C++ 实现中是:

node = node->right;

其中节点的类型为pTreeNode

pTreeNode (std::shared_ptr&lt;TreeNode&gt;) 上调用 = 运算符时,将调用 node 的析构函数。指向底层TreeNode 的共享指针的数量为1,因此TreeNode 被销毁以释放其内存。当TreeNode(默认)析构函数被调用时,它的每个成员都会被销毁。这肯定会导致pTreeNode right 成员被销毁。问题是node-&gt;right 是分配给node 的内容。在测试我的 BST 时,它似乎工作正常,没有错误/内存泄漏。

  • 我在做什么不安全吗?
  • 如果不安全,我可以做些什么来解决这个问题?

我认为可能有效的“hack”是创建另一个指针以增加其引用计数。这会是一个适当的解决方案吗?

//increase reference to node->right by 1 so it doesn't get destroyed
pTreeNode temp(node->right);
node = node->right;

【问题讨论】:

  • 我不熟悉共享指针。我的建议是查看析构函数是在赋值之前还是之后调用的。我的猜测是它是在之后调用的,这意味着有一个指向前node-&gt;right 的指针已经增加了引用计数。因此,子节点不会被破坏。
  • 相关 Herb Sutter youtu.be/JfmTagWcqoE?t=887
  • @CaptainGiraffe 哇,我不知道有类似 shared_ptr alias ..using 的东西会阻止解决方法
  • shared_ptr 可以安全使用...复制和移动操作已正确实施以按预期工作。所以只要你不使用原始的底层指针做奇怪的事情,代码应该可以正常工作。
  • 没有什么可分享的,为什么还要使用shared_ptr?使用 unique_ptr。

标签: c++ c++11 binary-search-tree shared-ptr smart-pointers


【解决方案1】:

你显然是在假设,在

node = right;

shared_ptr 的赋值运算符可以在完成对right 的读取之前减少节点的计数(或者在增加right 使用的引用计数之前)。但是,根据 cppreference,使用

template<typename T>
template<typename U>
std::shared_ptr<T> &std::shared_ptr<T>::operator =(const std::shared_ptr<U> &);

作为

node = right; // std::shared_ptr<treeNode>

等价于

std::shared_ptr<treeNode>(right).swap(node);

这是安全的,因为right 被复制之前 node 的旧值被销毁。顺便说一句,我自己实现了一个共享指针,并且我看到“清理”旧值是我在operator = 中所做的最后件事,正是为了避免此类问题。 p>

【讨论】:

    【解决方案2】:
    • 我在做什么不安全吗?

    不,据我所知是安全的。 leftright 节点实例将保持活动状态,直到它们的引用计数降至零。

    • 如果不安全,我可以做些什么来解决这个问题?

    您应该注意的唯一相关事情是不要在树实现之外分发任何节点作为shared_ptr。这些应该是std::weak_ptr 或原始指针。

    【讨论】:

    • 如果您希望外部代码能够独立于节点仍在搜索树内的事实使用该节点数据,那么在外部提供std::shared_ptr 并没有错。例如,如果您想在处理之前删除一个节点。
    • 将节点指针暴露给外部客户端是首先使用 shared_ptr 的唯一原因。否则可以使用便宜得多的 unique_ptr。
    猜你喜欢
    • 1970-01-01
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    • 2017-05-10
    • 1970-01-01
    相关资源
    最近更新 更多