Alireza Arbabi
Alireza Arbabi

Reputation: 23

deep copy in struct pointers

in this code, I think it must do deep copy because I'm passing pointers, but it doesn't. I think it must print 3, but print 0. what should I do to solve this? i want to have a deep copy instead of a shallow copy.

 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;
}

Upvotes: 0

Views: 708

Answers (2)

user4581301
user4581301

Reputation: 33952

The simple C-ish way: We ad a function that recursively clones the nodes

#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;
}

More complicated canonical C++ way using The Rule Of Three and The Copy and Swap Idiom

#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;
}

All of the nodes are now self-managing.

But, my opinion, the nodes should be kept as dumb as they are in the simple example. They shouldn't need to know about the tree at all. Knowledge of the tree should be in a Tree class that uses the nodes as simple containers. The Tree class should do all of the management of the nodes--creating, copying, cloning, deleting--because only it knows everything necessary to represent the tree. The users of the tree shouldn't even know nodes exist. They put data into the tree, take data out of the tree, and observe the tree without caring how the tree works and being able to so something stupid like

delete a_node;

and blow the crap out of data the tree isn't done with yet.

The Tree class preferably works iteratively rather than recursively so that the trees can be arbitrarily sized. Unless you're really careful recursing through a large tree to clone it, delete it, display it, or pretty much anything else you do with a tree runs the risk of exhausting automatic storage and causing what's typically called a Stack Overflow.

Upvotes: 0

just use

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

instead of

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

and it will print 3.

This is because...

when you do node1 = node2; int the test1, you assign the pointer itself, not the structure pointed to by the pointer.When the function ends, the parameters node1 and node2 will be destroyed, so you have done nothing...

Upvotes: -1

Related Questions