【问题标题】:deep copy in struct pointers结构指针中的深拷贝
【发布时间】:2022-01-09 09:27:09
【问题描述】:

在这段代码中,我认为它必须进行深度复制,因为我正在传递指针,但事实并非如此。 我认为它必须打印 3,但打印 0。我应该怎么做才能解决这个问题?我想要深拷贝而不是浅拷贝。

 struct node{
    int number = 0;
    struct node* right_child = NULL;
    struct node* left_child = NULL;
  };

void test(struct node* node1 , struct node* node2){
    node1 = node2;
}

int main(){
    struct node* a1 = new struct node;
    struct node* a2 = new struct node;
    a2->number = 3;
    test(a1 , a2);
    cout << a1->number;
}

【问题讨论】:

  • 我怎样才能拥有深拷贝?在这种情况下我需要深拷贝
  • 见朱利安的回答
  • 你甚至没有浅拷贝,只有指针的拷贝,什么都不做。你打算node1-&gt;number = node2-&gt;number;吗?
  • 只需实现一个 clone 方法来进行深度复制,递归可以提供帮助。
  • 除了node2node1 中的地址之外,没有任何内容被复制,并且由于node1node2test 范围内的局部变量,这在@987654329 之外没有任何影响@。实际上test 什么都不做。

标签: c++ deep-copy shallow-copy


【解决方案1】:

简单的 C-ish 方式:我们添加一个递归克隆节点的函数

#include <iostream>

struct node{
    int number = 0;
    node* right_child = nullptr; // nullptr safer than NULL
    node* left_child = nullptr;

};

node * clone(const node * src){
    if (src) { // there is a node. Copy it.
        return new node{src->number,
                        clone(src->right_child), // clone right
                        clone(src->left_child)}; // clone left
    }
    else { // no node. Nothing to do and end the branch
        return nullptr;
    }
}

void test(node*& node1, // reference to pointer
          node* node2){
    delete node1; // Free storage currently used by node1
                  // But what of the nodes that could be in its tree? 
                  // Think on this. Ask another question if necessary.
                  // this is where the simple way starts to fall down
    node1 = clone(node2);
}

int main(){
    struct node* a1 = new node; // wasted memory. We're about to replace it.

    // need a more complicated tree to prove deep copy works
    struct node* a2 = new node{3,
                               new node{4, nullptr, nullptr}, // a right node
                               nullptr}; // no left node
    // a2->number = 3; done above now
    test(a1 , a2);
    std::cout << a1->number << ' '
              << a1->right_child->number;

    delete a1; // clean up
    delete a2;
}

使用The Rule Of ThreeThe Copy and Swap Idiom 的更复杂的规范C++ 方式

#include <iostream>
#include <algorithm> // for std::swap

struct node{
    int number = 0;
    node* right_child = nullptr; // nullptr safer than NULL
    node* left_child = nullptr;

    static node * clone(node * src)
    {
        if (src)
        {
            return new node(*src);
        }
        return nullptr;
    }
    // generic constructor
    node(int number = 0,
         node* right_child = nullptr,
         node* left_child = nullptr):
             number(number),
             right_child(right_child),
             left_child(left_child)
    {

    }
    //copy constructor
    node (const node & src):
          number(src.number),
          right_child(clone(src.right_child)),
          left_child(clone(src.left_child))
    {

    }
    // assignment operator via copy and swap.
    node & operator=(node src)
    {
        std::swap(number, src.number);
        std::swap(right_child, src.right_child);
        std::swap(left_child, src.left_child);
        return *this;
    }
    // destructor
    ~node()
    {
        delete right_child;
        delete left_child;
    }
};

void test(node* node1,
          node* node2){
    *node1 = *node2; // now, thanks to all of the infrastructure above, we can 
                     // assign nodes with the dumb old = operator. All we have to do 
                     // is dereference the pointers.
}

int main(){
    struct node* a1 = new node; // wasted memory. We're about to replace it.

    // need a more complicated tree to prove deep copy works
    struct node* a2 = new node{3,
                               new node{4, nullptr, nullptr}, // a right node
                               nullptr}; // no left node
    // a2->number = 3; done above now
    test(a1 , a2);
    std::cout << a1->number << ' '
              << a1->right_child->number;

    delete a1; // clean up
    delete a2;
}

所有节点现在都可以自我管理。

但是,我认为,节点应该保持与简单示例中一样的愚蠢。他们根本不需要知道这棵树。树的知识应该在使用节点作为简单容器的Tree 类中。 Tree 类应该对节点进行所有管理——创建、复制、克隆、删除——因为只有它知道代表树所需的一切。树的用户甚至不应该知道节点的存在。他们将数据放入树中,从树中取出数据,然后观察树而不关心树是如何工作的,并且能够做一些像这样愚蠢的事情

delete a_node;

然后把树还没有处理完的数据扔掉。

Tree 类最好以迭代方式而不是递归方式工作,以便可以任意调整树的大小。除非您非常小心地通过一棵大树递归来克隆它、删除它、显示它,或者您对树执行的任何其他操作,否则会冒着耗尽自动存储空间并导致通常称为 Stack Overflow 的风险。

【讨论】:

    【解决方案2】:

    随便用

    void test(struct node *node1, struct node *node2) { *node1 = *node2; }
    

    而不是

    void test(struct node *node1, struct node *node2) { node1 = node2; }
    

    它会打印 3。

    这是因为……

    当你做node1 = node2; int test1时,你赋值的是指针本身,而不是指针指向的结构体。当函数结束时,参数node1node2会被销毁,这样你就完成了没什么……

    【讨论】:

    • 这不是深拷贝,会导致两棵树指向完全相同的节点。迟早会编辑一棵树,您会在“复制”中看到相同的更改,或者释放树节点的清理代码将释放两棵树中的节点,从而使程序中的树指向无效节点。
    猜你喜欢
    • 1970-01-01
    • 2016-12-18
    • 2010-12-28
    • 2010-12-16
    • 1970-01-01
    • 2015-07-21
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    相关资源
    最近更新 更多