开发者

Binary Tree Copy Constructor

I have a school assignment which requires me to create a binary tree in c++, complete with all the usual overloaded operators (=, ==, !=, copy, destroy, etc). I am currently struggling to program the copy constructor method. In this assignment, I am being asked NOT to use the binary tree class' insert method in either the operator= or copy constructor methods. Here's the binary tree class header file:

#ifndef BINTREE_H
#define BINTREE_H
#include <iostream>
#include "nodedata.h"
using namespace std;

class BinTree
{

public:
    BinTree();                           // constructor
    BinTree(const BinTree &);            // copy constructor, calls copyHelper
    ~BinTree();                          // destructor, calls makeEmpty
    bool insert(NodeData*);              // insert method, inserts new nodes
    bool isEmpty() const;                // true if tree is empty, otherwise false

private:
    struct Node
    {
开发者_如何学Go        NodeData* data;              // pointer to data object
        Node* left;                  // left subtree pointer
        Node* right;                 // right subtree pointer
    };
    Node* root;                          // root of the tree
    Node& copyHelper(const Node*);       // copies the tree recursively
    void makeEmpty(Node*);               // deletes nodes recursively
};

#endif

And here's what I've got so far for the copy constructor:

BinTree::BinTree(const BinTree &otherTree)
{
    root = copyHelper(otherTree.root); // 1
}

Node& BinTree::copyHelper(const Node* other) // 2
{
    if(other == NULL)
    {
        return NULL; // 3
    }
    Node newNode; // 4
    if(other.data == NULL)
    {
        newNode.data = NULL; // 5
    }
    else
    {
        NodeData newNodeData(*other->data); // 6
        newNode.data = newNodeData; // 7
    }
    newNode.left = copyHelper(other.left); // 8
    newNode.right = copyHelper(other.right); // 9
    return newNode; // 10
}

This is causing all manner of compile errors, and I don't understand any of them. Here's what I THINK should be happening: •Overview: The entire tree will be copied from the ground up recursively. Each node should already contain data and links to all subsidiary nodes (if they exist) when it is returned to the node above it.

  1. Since root is, by definition, a pointer to a Node object, there should be no issue with assigning it to a function which returns a Node object. However, the compiler is giving me a conversion error here.
  2. The copyHelper function, which is called recursively, takes a pointer to one of the original nodes as an argument, and returns a copy of this node.
  3. If there is no original node, then there's no point in building a copy of it, so a NULL value is returned.
  4. newNode will eventually become a copy of "other".
  5. If "other" does not have any NodeData linked to it, then newNode's data pointer will link to NULL instead.
  6. Otherwise, a new NodeData called "newNodeData" will be created, using the NodeData copy constructor, which is called by dereferencing other's NodeData pointer.
  7. newNode's data pointer now points to newNodeData, which now contains the same string as other's NodeData object.
  8. newNode's left pointer will point to another node, which is created recursively by calling copyHelper on whatever Node other's left pointer is assigned to.
  9. newNode's right pointer will point to another node, which is created recursively by calling copyHelper on whatever Node other's right pointer is assigned to.
  10. Only once this node and all the nodes beneath it have been copied and returned to it will it be returned itself.

Here are my existing questions:

  • I'm assuming that we only need to use -> instead of . when we're dereferencing a pointer. Is this correct?
  • I know sometimes we need to use the "new" statement when creating objects (see part 4), but I've typically only seen this done when creating pointers to objects. When, specifically are we supposed to be using the "new" statement?
  • If I were to create newNode as a pointer (IE: Node* newNode = new Node;), wouldn't this cause a pointer to a pointer when it was returned? Wouldn't that be less efficient than simply having the first pointer point directly to the returned Node object? Is there some technical reason I can't do it this way?
  • When is it advisable to take a pointer by reference as a parameter, instead of simply taking the pointer, or even the object itself? Is this relevant here?
  • The compiler seems to think I'm declaring a Node named BinTree::copyHelper instead of defining a function that returns a Node. How can I prevent this?

In general, I think I have the concepts down, but the syntax is completely killing me. I literally spent all day yesterday on this, and I'm ready to admit defeat and ask for help. What mistakes do you guys see in my code here, and how can I fix them?


This line:

Node newNode; // 4

Allocates a Node on the stack. When the function returns, the Node is no longer valid. It has gone out of scope. It may work for a while if another function call doesn't rewrite the stack.

You need to do a New on the node.


Node& copyHelper(const Node*);       // copies the tree recursively

Must that be a reference? It looks like it should be Node* instead.

In your declaration of copyHelper, the type Node is nested within BinTree but you fail to implement this. It should instead be

BinTree::Node* BinTree::copyHelper(const BinTree::Node* other)


  1. Yes, -> dereferences a pointer. It's a shortcut for *pointer.member.
  2. new creates object on the heap and returns a pointer - it does not create a pointer. Every time you create a object on the heap, you must use new.
  3. Node * newNode = new Node; assigns pointer to newly created Node to newNode. Node * is pointer to object, not pointer to pointer, which would be Node **. You cannot do Node newNode = new Node;, as Node * (pointer) is not convertible to Node (object).
  4. When you take parameter as reference, you are (generally) guaranteed that the parameter is not not. You also does not need to dereference the reference and you can just use . instead of ->.
  5. "The compiler seems to think I'm declaring a Node named BinTree::copyHelper instead of defining a function that returns a Node. How can I prevent this?" - how did you come to such a conclusion?


After much fretting and pulling of hair, I have rewritten my copy constructor and gotten it to compile. There may well still be errors in it, but I'm feeling a lot better about it now thanks in part to the tips posted here. Thanks again! Here's the modified code:

BinTree::BinTree(const BinTree &otherTree)
{
    root = copyHelper(otherTree.root); // Starts recursively copying Nodes from
                                       // otherTree, starting with root Node.
}

BinTree::Node* BinTree::copyHelper(const Node* other) // Needed BinTree at beginning
                                                      // due to nested Node struct
                                                      // in BinTree class.
{
    if(other == NULL)
    {
        return NULL; // If there's no Node to copy, return NULL.
    }
    Node* newNode = new Node;  // Dynamically allocated memory will remain after
                               // function is no longer in scope.  Previous newNode
                               // object here was destroyed upon function return.

    if(other->data == NULL) // Other is a pointer to data, which is also a pointer.
                            // -> dereferences other, and provides the item at the
                            // memory address which other normally points to.  It
                            // just so happens that since data is a pointer itself,
                            // it can still be treated as such.  I had stupidly been
                            // attempting to use . instead of ->, because I was afraid
                            // the -> would dereference data as well.  If I actually
                            // wanted to DO this, I'd have to use *other->data, which
                            // would dereference all of other->data, as if there were
                            // parenthesis around it like this: *(other->data).
                            // Misunderstanding this was the source of most of the
                            // problems with my previous implementation.
    {
        newNode->data = NULL; // The other Node doesn't contain data,
                              // so neither will this one.
    }
    else
    {
        NodeData* newNodeData = new NodeData; // This needed to be dynamically
                                                  // allocated as well.

        *newNodeData = *other->data; // Copies data from other node.
        newNode->data = newNodeData;
    }
    newNode->left = copyHelper(other->left); // Recursive call to left node.
    newNode->right = copyHelper(other->right); // Recursive call to right node.
    return newNode; // Returns after child nodes have been linked to newNode.
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜