5

I am trying to create a deep copy of my binary tree data structure in C++. The problem is the code I am using only seems to be giving me a shallow copy (which seems to cause problems with my deconstructor).

the code below is my binary tree copy constructor:

BinaryTreeStorage::BinaryTreeStorage(const BinaryTreeStorage &copytree):root(NULL)
{
    root = copytree.root;
    copyTree(root);
}

BinaryTreeStorage::node* BinaryTreeStorage::copyTree(node* other)
{
    //if node is empty (at bottom of binary tree)
    /*
        This creates a shallow copy which in turn causes a problem 
        with the deconstructor, could not work out how to create a 
        deep copy.
    */
    if (other == NULL)
    {
        return NULL;
    }

    node* newNode = new node;

    if (other ->nodeValue == "")
    {
        newNode ->nodeValue = "";
    }

    newNode->left = copyTree(other->left);
    newNode->right = copyTree(other->right); 

    return newNode;
}

Any help would be appreciated. Thanks

Here is the deconstructor that throws a memory exception (which i believe is because of the shallow copy i do above)

BinaryTreeStorage::~BinaryTreeStorage(void)
{
    try
    {
        destroy_tree();//Call the destroy tree method
        delete root;//delete final node
    }
    catch(int &error)
    {
        cout << "Error Message : " << error << endl;
    }
}
void BinaryTreeStorage::destroy_tree()
{
    destroy_tree(root);
}
void BinaryTreeStorage::destroy_tree(node *leaf)
{
  if(leaf!=NULL)
  {
    //Recursively work way to bottom node 
    destroy_tree(leaf->left);
    destroy_tree(leaf->right);
    //delete node
    delete leaf;
 }
}
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
brian4342
  • 1,265
  • 8
  • 33
  • 69
  • Assuming nodeValue is string, you need to allocate memory for newNode->nodeValue and copy value from other->nodeValue to newNode->nodeValue – rt2800 May 07 '12 at 12:25
  • and how do i do that? sorry for the noob responce :P – brian4342 May 07 '12 at 12:30
  • Next to deep copying the root (see answers), you are also not passing the value if it is not an empty string. You probably want `node* newNode = new node(*other);` – stefaanv May 07 '12 at 12:31
  • that didnt seem to work either still throws a memory exception - Unhandled exception at 0x5f0865ca (msvcr100d.dll) in binarytree.exe: 0xC0000005: Access violation reading location 0xfeeefee2. – brian4342 May 07 '12 at 12:36
  • It's called "destructor". "Deconstructing" is what you do in philosophy class. – Kerrek SB May 07 '12 at 12:47
  • 1
    @Kerrek SB thanks your comment has fixed all my problems :| – brian4342 May 07 '12 at 12:51

2 Answers2

5

You're not performing a deep copy of your root node, only its children.

Shouldn't it be:

root = copyTree(copytree.root);

?

EDIT: In addition, you destroy the root twice:

destroy_tree();//Call the destroy tree method

//once here
//remove this line
delete root;//delete final node

and

if(leaf!=NULL)
{
   //Recursively work way to bottom node 
   destroy_tree(leaf->left);
   destroy_tree(leaf->right);

   //one more time here
   delete leaf;
}

If you only do one of these fixes, the problem won't be solved.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • I tried that, there was no errors until it hit the deconstructor, gonna post my deconstructor In a second. – brian4342 May 07 '12 at 12:29
  • @BrianPeach have you tried debugging the copy constructor? Are the nodes built correctly. – Luchian Grigore May 07 '12 at 12:33
  • It do perform deep copy but not save it, which is in fact a case of memory leak. – wuliang May 07 '12 at 12:33
  • @BrianPeach see edited answer. You're deleting the root twice. – Luchian Grigore May 07 '12 at 12:37
  • @wuliang a deep copy is when the `this` object copies all information from a parameter. If the info is not saved in `this`, how can you call it a deep copy? – Luchian Grigore May 07 '12 at 12:38
  • @Luchian Grigore the nodes build correctly because i can print out the copy of the binary tree with no problems – brian4342 May 07 '12 at 12:39
  • @Luchian Grigore i just commented out "delete root;" and still got the same memory exception – brian4342 May 07 '12 at 12:42
  • @Luchian Grigore yea was just testing your answer – brian4342 May 07 '12 at 12:42
  • @BrianPeach so you kept **both** changes? Post the full code to www.ideone.com then. – Luchian Grigore May 07 '12 at 12:44
  • @Luchian Grigore i am running both of your fixes and getting this error - Unhandled exception at 0x5f0fad84 (msvcp100d.dll) in binarytree.exe: 0xC0000005: Access violation writing location 0xfeeefeee. – brian4342 May 07 '12 at 12:45
  • @BrianPeach post the full code to ideone and get it to crash there. – Luchian Grigore May 07 '12 at 12:47
  • @BrianPeach don't edit the question here with radical changes, or posted answers won't make sense any more. Use ideone as I asked you. You have to help us help you. – Luchian Grigore May 07 '12 at 12:49
  • @BrianPeach so post the header file here - http://ideone.com/21mGJ - click on clone and edit the code. It has to compile and crash. – Luchian Grigore May 07 '12 at 12:53
  • @BrianPeach almost there. I've modified it a bit - http://ideone.com/ldF7D - but there's no crash. Add the code in `main` which generates the crash. – Luchian Grigore May 07 '12 at 12:58
  • @Luchian Grigore http://ideone.com/uLof7 this is the main method (you can comment out the part for ArrayStorage to make it compile. – brian4342 May 07 '12 at 13:05
  • in ideone.com/uLof7 on line 97, you do `ArrayStorage arrayStorage4 = arrayStorage3;` which does a shallow copy – stefaanv May 07 '12 at 13:27
  • @stefaanv the code that is in the main i am not allowed to change but i have to create the classes to work with it though you are right that also creates a shallow copy please see the code that i have used to try and make it deep :S http://stackoverflow.com/questions/10482998/create-a-deep-copy-of-an-array-c – brian4342 May 07 '12 at 13:34
1

Actually, I think we can directly using the copy constructor to recursively deep copy the tree. Suppose the class is defined as follows:

class TreeNode {
public:
  TreeNode() : value(), count(0), left(nullptr), right(nullptr) {}
  TreeNode(const TreeNode &);

  ~TreeNode();

  TreeNode &operator=(const TreeNode &);

  // Other members...

private:
  std::string value;
  int count;
  TreeNode *left;
  TreeNode *right;
  // Note that the valid state for the `left` and `right` pointer is either
  // `nullptr` or a subtree node. So that we must check these pointers every
  // time before dereferencing them.
};

Then the copy constructor is

TreeNode::TreeNode(const TreeNode &n)
    : value(n.value), count(n.count), left(nullptr), right(nullptr) {
  if (n.left)
    left = new TreeNode(*n.left);  // recursively call copy constructor
  if (n.right)
    right = new TreeNode(*n.right);  // recursively call copy constructor
}

The recursion will be stopped at the leaf node, since both its two children are nullptr.

And so does the destructor.

TreeNode::~TreeNode() {
  delete left;  // recursively call destructor on left subtree node
  delete right;  // recursively call destructor on right subtree node
}

When left or right is nullptr, delete will do nothing, so that the recursion will be stopped.

You can see the complete code from here.

Jaege
  • 1,833
  • 4
  • 18
  • 32