【问题标题】:Rule-of-five for a BST class in C++C++ 中 BST 类的五规则
【发布时间】:2020-05-20 21:59:40
【问题描述】:

我正在实现一个二叉搜索树类,并且想知道我的移动/复制构造函数和赋值运算符是否正确实现。 (似乎可以正常工作,但这是我第一次实现这些构造函数和赋值运算符,恐怕我错过了一些东西。)

这里是代码(也是in an online compiler): 编辑:这是基于@Alex Larionov 评论的updated code

#include <memory>
#include <iostream>

class BinarySearchTree {

public:
    BinarySearchTree();
    BinarySearchTree(int value);
    BinarySearchTree(const BinarySearchTree& other_tree);
    BinarySearchTree(BinarySearchTree&& other_tree);
    BinarySearchTree& operator=(const BinarySearchTree& other_tree);
    BinarySearchTree& operator=(BinarySearchTree&& other_tree);
    ~BinarySearchTree() = default;

    void clear();

    inline int size() const {
        return tree_size;
    }
    inline bool empty() const {
        return tree_size == 0;
    }

private:
    struct Node {
        int val;
        std::unique_ptr<Node> left = nullptr;
        std::unique_ptr<Node> right = nullptr;

        Node(const int value) :
        val{value},
        left{nullptr},
        right{nullptr}
        {}
    };

    std::unique_ptr<Node> root;
    int tree_size;

    void deep_copy_tree(std::unique_ptr<Node>& dest_node, const std::unique_ptr<Node>& source_node);

};


BinarySearchTree::BinarySearchTree() : root{nullptr}, tree_size{0} {
    std::cout << "BinarySearchTree() constructor\n";
}

BinarySearchTree::BinarySearchTree(int value) : root{std::make_unique<Node>(value)}, tree_size{1} {
    std::cout << "BinarySearchTree(int value) constructor\n";
}

BinarySearchTree::BinarySearchTree(const BinarySearchTree& other_tree) : root{nullptr}, tree_size{0} {
    std::cout << "Copy constructor\n";
    if (other_tree.tree_size == 0) return;
    tree_size = other_tree.tree_size;
    deep_copy_tree(root, other_tree.root);
}

BinarySearchTree::BinarySearchTree(BinarySearchTree&& other_tree) :
root(std::exchange(other_tree.root, nullptr)), tree_size(std::exchange(other_tree.tree_size, 0)) {
        std::cout << "Move constructor\n";
}

BinarySearchTree& BinarySearchTree::operator=(const BinarySearchTree& other_tree) {
    std::cout << "Copy assignment operator\n";
    clear();
    tree_size = other_tree.tree_size;
    deep_copy_tree(root, other_tree.root);
    return *this;
}

// EDIT: updated based on @Alex Larionov comment
BinarySearchTree& BinarySearchTree::operator=(BinarySearchTree&& other_tree) {
    std::cout << "Move assignment operator\n";
    clear();
    tree_size = other_tree.tree_size;
    other_tree.tree_size = 0;
    root = std::move(other_tree.root);

    return *this;
}
/*BinarySearchTree& BinarySearchTree::operator=(BinarySearchTree&& other_tree) {
    std::cout << "Move assignment operator\n";
    clear();
    tree_size = other_tree.tree_size;
    deep_copy_tree(root, other_tree.root);

    other_tree.tree_size = 0;
    other_tree.root = nullptr;

    return *this;
}*/


void BinarySearchTree::clear() {
    root = nullptr;
    tree_size = 0;
}

void BinarySearchTree::deep_copy_tree(std::unique_ptr<Node>& dest_node, const std::unique_ptr<Node>& source_node) {
    if (!source_node) return;
    dest_node = std::make_unique<Node>(source_node->val);
    deep_copy_tree(dest_node->left, source_node->left);
    deep_copy_tree(dest_node->right, source_node->right);
}


int main()
{
    BinarySearchTree myBST1(5);
    BinarySearchTree myBST2 = myBST1; // copy constructor

    BinarySearchTree myBST3(4);
    myBST3 = myBST1; // copy assignment

    std::cout << "myBST3.empty() before move: " << myBST3.empty() << '\n';
    BinarySearchTree myBST4(std::move(myBST3)); // move constructor
    std::cout << "myBST3.empty() after move: " << myBST3.empty() << '\n';

    std::cout << "myBST4.empty() before move assignment: " << myBST4.empty() << '\n';
    myBST2 = std::move(myBST4); // move assignment
    std::cout << "myBST4.empty() after move assignment: " << myBST4.empty() << '\n';


    return 0;
}

【问题讨论】:

  • 如果代码有效,更好的发布位置是Code Review
  • BinarySearchTree&amp; BinarySearchTree::operator=(BinarySearchTree&amp;&amp; other_tree)你必须从other_tree移动数据而不是复制它。
  • @ThomasMatthews,我在这里做过:codereview.stackexchange.com/questions/242501/…。但它包含更多方法,我还没有收到关于构造函数/赋值运算符的具体评论。
  • @AlexLarionov,对。谢谢!
  • @AlexLarionov,我更新了代码,现在移动分配看起来正确吗?

标签: c++ binary-search-tree copy-constructor move-constructor rule-of-five


【解决方案1】:

复制构造函数默认初始化,然后检查other_tree 是否为空以避免深度复制它。但是您已经在deep_copy_tree 中进行了检查。为什么不直接用它初始化呢?

BinarySearchTree::BinarySearchTree(const BinarySearchTree& other_tree) : tree_size{other_tree.tree_size} {
    std::cout << "Copy constructor\n";
    deep_copy_tree(root, other_tree.root);
}

为了更进一步,我会让deep_copy_tree 返回而不是使用外参数(并且还删除“_tree”;它已经在“Tree”类中)。

std::unique_ptr<Node> BinarySearchTree::deep_copy(const std::unique_ptr<Node>& source_node) {
    if (!source_node) return nullptr;
    auto dest_node = std::make_unique<Node>(source_node->val);
    dest_node->left = deep_copy(source_node->left);
    dest_node->right = deep_copy(source_node->right);
    return dest_node;
}

这样你也可以在初始化列表中初始化root

BinarySearchTree::BinarySearchTree(const BinarySearchTree& other_tree) : root(deep_copy_tree(other_tree.root)), tree_size{other_tree.tree_size} {
    std::cout << "Copy constructor\n";
}

在移动构造函数中,您不需要使用std::exchange。事实上,对于root,使用std::move(other_tree.root) 也是如此(从unique_ptrs 移动到nullptrs)。

在复制赋值运算符中,您可能想要检查自赋值。

if (this != &other_tree)

您也不需要在任一赋值运算符中使用 clear,因为赋值给 unique_ptr 会有效地破坏它所持有的值。

【讨论】:

  • 对于移动构造函数,我可以保持这样BinarySearchTree(BinarySearchTree&amp;&amp; other_tree) : root(std::move(other_tree.root)), tree_size(other_tree.tree_size) {}。我可以在构造函数主体中跳过设置other_tree.tree_size = 0 吗?移动后other_tree.tree_size 持有什么值是否重要?
  • 这完全取决于您。移出对象应该处于有效但未指定的状态; “有效”的含义由您决定。例如,从std::vector 移出的对象保证为空。我想将other_tree 的大小设置为零(这就是您目前对exchange 所做的)并没有什么坏处。
猜你喜欢
  • 2011-06-14
  • 1970-01-01
  • 2018-04-02
  • 1970-01-01
  • 2020-10-28
  • 2020-09-28
  • 2018-11-15
相关资源
最近更新 更多