为 C++ 二叉搜索树使用智能指针
Using smart pointers for C++ binary search tree
我已经用 C++ 实现了二叉搜索树。我没有使用裸指针指向节点 children,而是使用了 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中移除一个节点时,其中一种情况是该节点只有一个child。节点简单地被这个 child 替换,如下所示:
| remove node
node ---------> |
\ right
right
在类似的 Java 实现中,这可以编码为:
node = node.getRight();
在我的 C++ 实现中是:
node = node->right;
其中节点的类型为 pTreeNode
。
在 pTreeNode
(std::shared_ptr<TreeNode>
) 上调用 = 运算符时,将调用 node
的析构函数。指向底层 TreeNode
的共享指针的数量为 1,因此 TreeNode
被销毁以释放其内存。当 TreeNode
(默认)析构函数被调用时,它的每个成员都被销毁。这肯定会导致 pTreeNode right
成员被销毁。问题是 node->right
是分配给 node
的内容。在测试我的 BST 时,它似乎工作正常,没有 errors/memory 泄漏。
- 我这样做不安全吗?
- 如果它不安全,我可以做些什么来解决这个问题?
我认为可能有用的 'hack' 是制作另一个指针以增加其引用计数。这是一个合适的解决方案吗?
//increase reference to node->right by 1 so it doesn't get destroyed
pTreeNode temp(node->right);
node = node->right;
- Is what I am doing unsafe?
不,据我所知是安全的。 left
或 right
节点实例将保持活动状态,直到它们的引用计数降为零。
- If it is unsafe, what could I do to get around this problem?
您应该注意的唯一相关事项是不要将任何节点作为 shared_ptr
在树实现之外分发。这些应该是 std::weak_ptr
或原始指针。
你显然假设,在
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
的旧值被销毁。顺便说一句,我自己实现了一个共享指针,我看到 "cleaning up" 旧值是 last 我在 operator =
内部做的事情恰好是为了避免此类问题。
我已经用 C++ 实现了二叉搜索树。我没有使用裸指针指向节点 children,而是使用了 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中移除一个节点时,其中一种情况是该节点只有一个child。节点简单地被这个 child 替换,如下所示:
| remove node
node ---------> |
\ right
right
在类似的 Java 实现中,这可以编码为:
node = node.getRight();
在我的 C++ 实现中是:
node = node->right;
其中节点的类型为 pTreeNode
。
在 pTreeNode
(std::shared_ptr<TreeNode>
) 上调用 = 运算符时,将调用 node
的析构函数。指向底层 TreeNode
的共享指针的数量为 1,因此 TreeNode
被销毁以释放其内存。当 TreeNode
(默认)析构函数被调用时,它的每个成员都被销毁。这肯定会导致 pTreeNode right
成员被销毁。问题是 node->right
是分配给 node
的内容。在测试我的 BST 时,它似乎工作正常,没有 errors/memory 泄漏。
- 我这样做不安全吗?
- 如果它不安全,我可以做些什么来解决这个问题?
我认为可能有用的 'hack' 是制作另一个指针以增加其引用计数。这是一个合适的解决方案吗?
//increase reference to node->right by 1 so it doesn't get destroyed
pTreeNode temp(node->right);
node = node->right;
- Is what I am doing unsafe?
不,据我所知是安全的。 left
或 right
节点实例将保持活动状态,直到它们的引用计数降为零。
- If it is unsafe, what could I do to get around this problem?
您应该注意的唯一相关事项是不要将任何节点作为 shared_ptr
在树实现之外分发。这些应该是 std::weak_ptr
或原始指针。
你显然假设,在
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
的旧值被销毁。顺便说一句,我自己实现了一个共享指针,我看到 "cleaning up" 旧值是 last 我在 operator =
内部做的事情恰好是为了避免此类问题。