简体   繁体   中英

Difficulty inserting nodes to binary search tree using templates and template inheritance

What I am dealing with is I have created 3 template classes. I am trying to make a library for binary trees though I have limited knowledge of C++. I have implemented binary trees in C using structs but this time I wanted to take advantage of the object oriented programming C++ offers so I created classes for the node, the binary tree and the binary search tree.

I feel more comfortable showing all the code in case there is something else wrong that I didn't notice. So here it is. (Note: C++11 in use)

node_class.h

template <typename key_type, typename value_type>
class node_class {
public:
    node_class(key_type key, value_type value) {
        SetKey(key);
        SetValue(value);
        SetLeft(nullptr);
        SetRight(nullptr);
    }
    void SetKey(key_type key) {
        this->key = key;
    }
    void SetValue(value_type value) {
        this->value = value;
    }
    void SetLeft(node_class <key_type, value_type> *left) {
        this->left = left;
    }
    void SetRight(node_class <key_type, value_type> *right) {
        this->right = right;
    }
    key_type GetKey() {
        return this->key;
    }
    value_type GetValue() {
        return this->value;
    }
    node_class <key_type, value_type> *GetLeft() {
        return this->left;
    }
    node_class <key_type, value_type> *GetRight() {
        return this->right;
    }
private:
    key_type key;
    value_type value;
    node_class <key_type, value_type> *left;
    node_class <key_type, value_type> *right;
};

binary_tree_class.h

template <typename key_type, typename value_type>
class binary_tree_class {
public:
    binary_tree_class() {
        SetRoot(nullptr);
    }
    // ...
protected:
    void SetRoot(node_class <key_type, value_type> *root) {
        this->root = root;
    }
    node_class <key_type, value_type> *GetRoot() {
        return this->root;
    }
private:
    node_class <key_type, value_type> *root;
    // ...
};

binary_search_tree_class.h

template <typename key_type, typename value_type>
class binary_search_tree_class : public binary_tree_class <key_type, value_type> {
public:
    binary_search_tree_class() {

    }
    void Insert(key_type key, value_type value) {
        Insert(key, value, this->GetRoot());
    }
    void Insert(key_type key, value_type value, node_class <key_type, value_type> *node) {
        if (node == nullptr) {
            node = new node_class <key_type, value_type> (key, value);
        }
        if (key > node->GetKey()) {
            Insert(key, value, node->GetRight());
        } else if (key < node->GetKey()) {
            Insert(key, value, node->GetLeft());
        }
    }
    // ...
};

As far as the Insert function is conserned, I read that I have to pass the node parameter by reference to pointer in order for the changes to the tree to take place (Correct me if I am wrong). That being said I have to change the function protorype from this

void Insert(key_type key, value_type value, node_class <key_type, value_type> *node)

into this

void Insert(key_type key, value_type value, node_class <key_type, value_type> *&node)

After I did that I was completely unable to figure out what is wrong so I am still stuck with the following error from g++

In file included from main.cpp:1:0:
binary_search_tree_class.h: In instantiation of ‘void binary_search_tree_class<key_type, value_type>::Insert(key_type, value_type) [with key_type = int; value_type = int]’:
main.cpp:5:31:   required from here
binary_search_tree_class.h:11:9: error: invalid initialization of non-const reference of type ‘node_class<int, int>*&’ from an rvalue of type ‘node_class<int, int>*’
   Insert(key, value, this->GetRoot());
         ^
binary_search_tree_class.h:13:7: note:   initializing argument 3 of ‘void binary_search_tree_class<key_type, value_type>::Insert(key_type, value_type, node_class<key_type, value_type>*&) [with key_type = int; value_type = int]’
  void Insert(key_type key, value_type value, node_class <key_type, value_type> *&node) {

In the files above I couldn't write the include directives because the # symbol was causing problems with the indentation but I think you get it.

Issue

The issue starts from the method:

node_class<>* binary_search_tree<>::GetRoot();

It returns the copy pointer of the variable member root .

In other words, when you invoke the method GetRoot() you obtain the address location of the root node, and potentially save that address into another pointer variable.

Just to give you an idea. Let us suppose there is the root node somewhere in the memory:

 0x01 [...]
 0x02 [RootNode]
 0x03 [...]

Your binary tree will keep the address of the root node and store it in a pointer (which is a variable anyway).

The layout memory will be something like:

0x01 [...]
0x02 [RootNode]
0x03 [...]
0x04 [binary_tree::root = 0x02]

Let us suppose we invoke in order to take the root address: GetRoot() it will actually return the address of RootNode and not a reference to the member variable binary_tree:root which points to the RootNode .

So when you invoke GetRoot() with:

void foo() {
  auto p = binary_tree.GetRoot();
}

The layout memory will be something like:

0x01 [...]
0x02 [RootNode]
0x03 [...]
0x04 [binary_tree::root = 0x02]
0x05 [...]
0x06 [p = 0x2]

As you can see in the scheme, p is a copy of root (variable member), but they are, indeed, two different pointers which actually point the same location memory.

So if you pass p as reference to a function which modifies it, then p will be modified but not the member variable ::root .

The scheme and code is quite schematic, but I hope they will help you to understand the situation. GetNode() acts like copying the member variables and not returning a reference to it.

Your compile error comes from the fact that you are not actually saving the return copied address into another pointer, but directly passing it as argument. Therefore a temporary expression is created and passed to method Insert . However a temporary expression cannot be identified by a location in the memory and so you cannot take a reference from it, that's why your compiler says to cannot take a reference from a temporary value.


How to fix it?

There are several methods to approach that problem.

My opinion you can change your class design: just make the member variable protected in order to be accessed. In that way you can obtain the "real" pointer and not a copying address value.


Simple suggestions

  • This may be personal, but I think that the notation with pointer reference is quite unreadable. If your function (or method) needs to modify the address of a pointer, just use: void foo(node_class**) (pointer to pointer).

  • Adding _class to each name class is quite redundant and make the code more verbose. A class name should be just node .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM