【问题标题】:Using unique_ptr instead of shared_ptr in BST在 BST 中使用 unique_ptr 而不是 shared_ptr
【发布时间】:2014-07-06 00:27:19
【问题描述】:

我正在尝试使用 unique_ptr 实现 BST。我得到了shared_ptr 的工作程序。如何改用 unique_ptr 来强制执行 BinarySearchTree 的单一所有权语义?

当我将 shared_ptr 替换为 unique_ptr 时,编译错误超出了我的理解范围。

#include <iostream>
#include <memory>

template<class T>
class BinarySearchTree{
    struct TreeNode;
    typedef std::shared_ptr<TreeNode> spTreeNode;
    struct TreeNode{
        T data;
        spTreeNode  left;
        spTreeNode  right;
        TreeNode(const T & value):data(value),left(nullptr),right(nullptr){}
    };



    spTreeNode root;
    bool insert(spTreeNode node);
    void print(const spTreeNode) const ;
public:
    BinarySearchTree();
    void insert( const T & node);
    void print()const;
};

template<class T>
BinarySearchTree<T>::BinarySearchTree():root(nullptr){}

template<class T>
void BinarySearchTree<T>::insert(const T & ref)
{
    TreeNode *node = new TreeNode(ref);
    if (root==nullptr)
    {
        root.reset(node);
    }
    else
    {
        spTreeNode temp = root;
        spTreeNode prev = root;
        while (temp)
        {
            prev = temp;
            if (temp->data < ref)
                temp = temp->right;
            else
                temp = temp->left;
        }
        if (prev->data  < ref)
            prev->right.reset(node);
        else
            prev->left.reset(node);
    }
}

template<class T>
void BinarySearchTree<T>::print()const
{
    print(root);
}

template<class T>
void BinarySearchTree<T>::print(const spTreeNode node)const
{
    if (node==nullptr)
        return;
    print(node->left);
    std::cout << node->data<< std::endl;
    print(node->right);
}

int main()
{
    BinarySearchTree<int> bst;
    bst.insert(13);
    bst.insert(3);
    bst.insert(5);
    bst.insert(31);
    bst.print();
    return 0;
}

编辑:编译错误以防万一有人感兴趣。警告:文字墙。

    prog.cpp: In instantiation of ‘void BinarySearchTree<T>::insert(const T&) [with T = int]’:
prog.cpp:75:18:   required from here
prog.cpp:39:27: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = BinarySearchTree<int>::TreeNode; _Dp = std::default_delete<BinarySearchTree<int>::TreeNode>]’
         spTreeNode temp = root;
                           ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from prog.cpp:2:
/usr/include/c++/4.8/bits/unique_ptr.h:273:7: error: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
prog.cpp:40:27: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = BinarySearchTree<int>::TreeNode; _Dp = std::default_delete<BinarySearchTree<int>::TreeNode>]’
         spTreeNode prev = root;
                           ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from prog.cpp:2:
/usr/include/c++/4.8/bits/unique_ptr.h:273:7: error: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
prog.cpp:43:18: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = BinarySearchTree<int>::TreeNode; _Dp = std::default_delete<BinarySearchTree<int>::TreeNode>]’
             prev = temp;
                  ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from prog.cpp:2:
/usr/include/c++/4.8/bits/unique_ptr.h:274:19: error: declared here
       unique_ptr& operator=(const unique_ptr&) = delete;
                   ^
prog.cpp:45:22: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = BinarySearchTree<int>::TreeNode; _Dp = std::default_delete<BinarySearchTree<int>::TreeNode>]’
                 temp = temp->right;
                      ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from prog.cpp:2:
/usr/include/c++/4.8/bits/unique_ptr.h:274:19: error: declared here
       unique_ptr& operator=(const unique_ptr&) = delete;
                   ^
prog.cpp:47:22: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = BinarySearchTree<int>::TreeNode; _Dp = std::default_delete<BinarySearchTree<int>::TreeNode>]’
                 temp = temp->left;
                      ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from prog.cpp:2:
/usr/include/c++/4.8/bits/unique_ptr.h:274:19: error: declared here
       unique_ptr& operator=(const unique_ptr&) = delete;
                   ^
prog.cpp: In instantiation of ‘void BinarySearchTree<T>::print() const [with T = int]’:
prog.cpp:79:15:   required from here
prog.cpp:59:15: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = BinarySearchTree<int>::TreeNode; _Dp = std::default_delete<BinarySearchTree<int>::TreeNode>]’
     print(root);
               ^
In file included from /usr/include/c++/4.8/memory:81:0,
                 from prog.cpp:2:
/usr/include/c++/4.8/bits/unique_ptr.h:273:7: error: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^
prog.cpp:63:6: error:   initializing argument 1 of ‘void BinarySearchTree<T>::print(BinarySearchTree<T>::spTreeNode) const [with T = int; BinarySearchTree<T>::spTreeNode = std::unique_ptr<BinarySearchTree<int>::TreeNode, std::default_delete<BinarySearchTree<int>::TreeNode> >]’
 void BinarySearchTree<T>::print(const spTreeNode node)const
          ^

【问题讨论】:

  • 也许发布这些编译错误会帮助您我们的理解。编辑:但是,您的一个函数按值(即复制)获取指针。但是unique_ptrs 只能移动。
  • @Quentin,我可以,但它们很长。无论如何,我会发布它们
  • 遍历树时,使用TreeNode*(无所有权),而不是unique_ptr&lt;TreeNode&gt;(独占所有权)。树,而不是遍历它的代码,拥有所有权。

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


【解决方案1】:

unique_ptrs 不可分配但可移动。我修改了您的示例,现在可以使用 unique_ptrs。请注意,我使用std::move 将内容从一个unique_ptr 移动到另一个。另外由于unique_ptr 不可复制,我通过引用而不是值在成员函数中传递unique_ptrs:

#include <iostream>
#include <memory>

template<class T>
class BinarySearchTree{
    struct TreeNode;
    typedef std::unique_ptr<TreeNode> spTreeNode;
    struct TreeNode{
        T data;
        spTreeNode  left;
        spTreeNode  right;
        TreeNode(const T & value):data(value),left(nullptr),right(nullptr){}
    };



    spTreeNode root;
    bool insert(spTreeNode &node);
    void print(const spTreeNode&) const ;
public:
    BinarySearchTree();
    void insert( const T & node);
    void print()const;
};

template<class T>
BinarySearchTree<T>::BinarySearchTree():root(nullptr){}

template<class T>
void BinarySearchTree<T>::insert(const T & ref)
{
    std::unique_ptr<TreeNode> node(new TreeNode(ref));
    if (root == nullptr) {
        root = std::move(node);
    } else {
        TreeNode* temp = root.get();
        TreeNode* prev = root.get();
        while (temp != nullptr) {
            prev = temp;
            if (temp->data < ref)
                temp = temp->right.get();
            else
                temp = temp->left.get();
        }
        if (prev->data < ref)
            prev->right = std::move(node);
        else
            prev->left = std::move(node);
    }
}

template<class T>
void BinarySearchTree<T>::print()const
{
    print(root);
}

template<class T>
void BinarySearchTree<T>::print(const std::unique_ptr<TreeNode> &node) const
{
    if(node == nullptr) return;
    print(node->left);
    std::cout << node->data<< std::endl;
    print(node->right);
}

int main()
{
    BinarySearchTree<int> bst;
    bst.insert(13);
    bst.insert(3);
    bst.insert(5);
    bst.insert(31);
    bst.print();
    return 0;
}

LIVE DEMO

【讨论】:

  • 所以基本上,当你需要“分配”时——使用 std::move 并且当你需要遍历时,使用 get()?
  • @IanMcGrath 基本上是的,但请注意 unique_ptr 也不能复制。因此,您不能通过值将其作为输入参数传递给函数,而是通过引用传递给函数。另外请注意,使用get 访问unique_ptr 的内容,请确保不要将此内容分配给另一个智能指针。
  • 谢谢。最后一个问题。如果我必须从某个函数返回一个节点,比如 getMaxValueNode(),我应该如何返回?
  • @IanMcGrath 恕我直言,您应该返回节点本身而不是 unique_ptr,因为您需要访问此节点。如前所述,unique_ptrs 不可复制,因此您不能返回已经存在的unique_ptr。但是请注意,如果您在函数体中构造 unique_ptr,然后在同一个函数中返回它,编译器足够聪明,可以移动此 unique_ptr,因此在这种情况下返回 unique_ptr 是允许的。
  • 没有get()有没有办法做到这一点?我们通过所有这些工作来使用unique_ptr,然后在遍历树时切换到原始ptr,这很麻烦。我明白你为什么这样做,我不知道如何避免它。有些事情告诉我,斯科特·迈耶斯会不赞成。
【解决方案2】:

模板风格的错误总是很可怕,但不要惊慌!所有这些“使用已删除功能”都是关于您试图复制unique_ptr,它的超能力来自只能移动。跳到每一行,分析情况:

您希望转让被指点者的所有权吗?然后通过右值引用传递唯一指针并将其移动到新的持有者。

// Take unique_ptr by rvalue reference
void TreeNode::setLeft(std::unique_ptr<TreeNode> &&node) {
    // Move it in a member variable
    left = std::move(node);
    // Now it's ours !
}

您只想引用指针吗?使用对 unique_ptr 的 const 左值引用,或从 unique_ptr.get() 传递一个 const 原始指针。

// Take raw const pointer
bool isNodeLeft(TreeNode const *node) const {
    // Look at it but don't disturb it
    return node->value <= value;
}

一旦所有这些都解决了,你要么有一个编译代码,要么你会解决一些其他错误,或者我会更新这个答案。

注意:TreeNode *node = new TreeNode(ref); 对异常安全大喊大叫。您应该使用 make_unique(来自 C++1y,或自己制作)。

这里是:

template <class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
    return unique_ptr<T>(new T(std::forward<Args>(args)...));
}

【讨论】:

  • 对不起,我不明白。对 C++11 智能指针来说真的很新。你能发布一两行代码吗?非常感谢。
  • 想通了。感谢您的指点(双关语)。现在,我不明白的是 make_unique 的东西,你能贴一行代码让我明白你的意思吗?
  • @IanMcGrath 他们来了!
  • @IanMcGrath 哦,好吧。谢谢,很高兴能帮到你:)
猜你喜欢
  • 2011-07-31
  • 2015-07-23
  • 2013-12-17
  • 2015-01-28
  • 2013-01-30
  • 2021-06-01
  • 2015-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多