unique_ptr 在 class 如何与他们合作

unique_ptr in class how to work with them

我正在用 C++ 实现 AVL 树,并为 children 使用 unique_ptr

struct Node 
{
    const int key;
    std::unique_ptr<Node> left, right;
    Node* parent;
    std::size_t height; ///< for avl tree.
    Node(const int key) : key(key), height(0) {}
};

class AVL
{
    std::unique_ptr<Node> root;
public:
    AVL(int rootKey) : root(std::unique_ptr<Node>(new Node(rootKey))) {
    }

    void insert(std::unique_ptr<Node> newNode) {
        std::unique_ptr<Node> & node = root;
        Node* parentWeak;
        while(node.get()) {
            parentWeak = node->parent;
            if (node->key == newNode->key)
                throw std::runtime_error("Key already present");
            if (node->key < newNode->key) 
                node = node->right;
            else
                node = node->left;
        }

        auto parent = parentWeak;
        const int key = newNode->key;

        if (parent == nullptr) {
            // there is no root
            root = std::move(newNode);
        } else {
            if (parent->key < newNode->key) {
                assert(NULL == parent->right.get());
                parent->right = std::move(newNode);
            } else {
                assert(NULL == parent->left.get());
                parent->left = std::move(newNode);
            }
        }

        // Now increment the height upto down.
        incrementHeight(key);
        // balance starting from parent upwards untill we find some dislanace in height 
        balance(parent, key); 
    }
};

我在第 node = node->right; 行收到编译器错误。这是正确的,因为只有 std::move 语义才有可能。但那是错误的,因为我只想遍历树,否则它只会从 child-list.

中删除它们

但是,我还需要 unique_ptr,因为它会传入函数 balance,因为它会修改指针和 re-balance 树。

如果我使用 shared_ptr 就可以了。但是,我不需要与他人分享所有权。还是我误解了所有权?

你的问题好像是对如何在实际程序中使用unique_ptr不了解造成的,这与所有权的概念有关。如果一个 something 拥有 一个对象,这意味着,只要 this something 一直拥有该对象,该 something 就负责保持该对象存活,并负责尽快销毁该对象 nothing 不再拥有该对象。

unique_ptrshared_ptr都可以用来拥有对象。您似乎已经意识到,区别在于 unique_ptr 指向的对象只能有一个所有者,而可能有多个 shared_ptr 对象 共享所有权 特定对象。如果 unique_ptr 被销毁或分配不同的值, 根据定义 它可以销毁它先前指向的对象,因为 unique_ptr 是单个(唯一)对象的所有者。

现在您必须考虑您的树:您可以对所有内容使用 shared_ptr,这很可能(似乎)有效,因为只要有对它们的引用,对象就会保持活动状态。如果 node 中确实存在您在方法中使用但未在 node 结构中声明的 parent 成员,那么您可能会创建引用循环,但会造成危险将对象保存太久(甚至永远,这称为内存泄漏),因为 C++ 中的 shared_ptr 纯粹是引用计数。两个包含 shared_ptr 的对象相互指向对方,即使没有其他指针指向它们,它们也会永远存在。在您的 shared_ptr 解决方案中,parent 成员似乎是一个 weak_ptr,这是解决此问题的明智方法,尽管可能不是最有效的方法。

您似乎想通过使用 unique_ptr 而不是 shared_ptr 来提高代码的性能和严格性,这被普遍认为是一个非常好的主意,因为它迫使您处理所有权更多细节。您选择树 拥有 根节点,每个节点 拥有 子节点是一个合理的设计。您似乎已经删除了 parent 指针,因为它不能是 unique_ptr,在这种情况下,节点将由其父节点 拥有childrens 它可能有,违反了 unique_ptr 指向的对象可能只有一个所有者的约束。此外,parent 成员不能是 weak_ptr,因为 weak_ptr 只能用于 shared_ptr 管理的对象。如果您想将设计从 shared_ptr 转换为 unique_ptr,您应该考虑将 weak_ptr 更改为 原始指针 。指向由 unique_ptr 管理的检测该对象到期的对象的非拥有指针不存在(使用典型的 C++ 内存管理无法有效实现)。如果您需要 属性 能够检测到非拥有指针是否过时,请继续使用 shared_ptr。跟踪非拥有指针的开销几乎与完全共享所有权语义一样大,因此标准库中没有中间地带。

最后,我们来讨论一下insert方法。 node 变量肯定不是您想要的。您正确地发现(可能是通过编译器错误消息)node 不能是 unique_ptr,因为这会剥夺树对象的所有权。事实上,让这个变量 引用 树中的根指针是正确的解决方案,因为此时你不想弄乱所有权,但只想能够掌握某个节点。但是将它声明为引用并不适合你想要使用它的方式,因为在C++中你不能重新设置一个引用。你所做的是将 node 声明为 只是 this->root 的另一个名称 ,因此如果你分配给 node,你将覆盖你的根节点指针。我确定这不是您想要的。相反,您希望 node 引用与之前引用不同的对象,因此它需要引用根节点并且可以引用其他对象。在 C++ 中,这意味着您需要一个指针(正如 Jarod42 在评论中所说)。循环扫描插入位置有两种选择:

  • 使用指向节点的原始指针而不是指向节点的 unique_ptr。由于您不需要所有权,因此指向节点的原始指针就足够了:您可以确保拥有指针 (this->root) 在您需要时保持活动状态,因此不存在对象消失的危险。
  • 使用指向 unique_ptr 的原始指针到节点。这本质上是您的方法,固定使用指针而不是引用。

正如您所说,稍后您需要 unique_ptr 将其传递给余额函数。如果 balance 函数像现在这样运行,并且需要一个 unique_ptr 参数,那么就会做出决定:在 node 中拥有原始指针的副本不会做你想要的,所以你需要指向 unique_ptr.

的指针