-1

I was assigned to create a sparse matrix. In the process, I got into a problem. I realized I could not access a member of a child class that was stored in a parent class.

I 'googled' my problem and what I got was that there were some casting problems. I tried it out and did not work either.

Sample code:

main.cpp

#include <iostream>

template <typename T>
class Node
{
public:                     // public for example purposes
    Node<T> *down, *right;

public:
    explicit Node(Node<T> *d, Node<T> *r) : down(d), right(r) {}
};

template <typename T>
class HNode : public Node<T>
{
private:
    unsigned idxValue;

public:
    HNode(unsigned iv) : idxValue(iv), Node<T>(nullptr, nullptr) {}
};

template <typename T>
class ENode : public Node<T>
{
public:                     // public for example purposes
    unsigned row;
    unsigned col;
    int value;

public:
    ENode(unsigned r, unsigned c, int v) : row(r), col(c), value(v), Node<T>(nullptr, nullptr)
    {}
};

int main(void)
{
    Node<int> *root;

    root = new Node<int>(nullptr, nullptr);

    root->right = new HNode<int>(0);
    root->down = new HNode<int>(0);
    root->right->down = new ENode<int>(0, 0, 10);


    std::cout << root->right->down->value << '\n';
}

This is the error I get:

error: no member named 'value' in 'Node<int>'
std::cout << root->right->down->value << '\n';
Miguel Yurivilca
  • 375
  • 5
  • 12
  • 5
    The "parent" doesn't know *anything* about the child (derived class). Why would you expect it to be able to access it's members? It could have a thousand derived classes with different members - how would it know (it doesn't)? – Jesper Juhl Jun 07 '19 at 16:53
  • 1
    Don't use `new`, use smart pointers like `std::unique_ptr` – Quimby Jun 07 '19 at 16:54
  • The answer to Jesper's comment is Runtime Reflection, something C++ doesn't support. – user4581301 Jun 07 '19 at 16:55
  • @Miguel Yurivilca The static type of the variables root, right, down is Node. * In the class specialization there is no data member value. – Vlad from Moscow Jun 07 '19 at 16:56
  • What you can do is have a virtual function in `Node` that returns a highly abstracted form of the value, a `std::string` for example` that can be supported by all possible children of `Node`. Then you can `std::cout << root->right->down->getvalue() << '\n';` Each child of `Node` will have to implement this function and perform the conversion of their specific value into the abstracted type. – user4581301 Jun 07 '19 at 16:58
  • Your question is a little strange, supposing without knowing how the tree was made few lines before why do you decide to get `root->right->down->value` ? Of course you can use a dynamic cast to check if `root->right->down` is a _ENode_ but that seems artificial. What is the _real_ goal, to print a (sub)Node ? in that case define a virtual method for that, probably _pure virtual_ on _Node_. – bruno Jun 07 '19 at 17:00

2 Answers2

3

root is a Node pointer. root->right is another Node pointer. root->right->down is--you guessed it--a Node pointer.

When you do [...]->value, you're dereferencing that Node pointer, which gives you a Node, and then trying to get the value member on it. But Node has no value member!

You could attempt to cast your Node pointer into an ENode pointer, using dynamic cast. Which would look like:

Node *n = root->right->down;
if(ENode *en = dynamic_cast<ENode*>(n)) {
    std::cout << en->value << std::endl;
} else {
    std::cout << "That's not an ENode!" << std::endl;
}

But to do this, you'll need to make Node polymorphic (which you can see detailed here).

Also note that in production code, you should be checking to make sure root, root->right and root->right->down are all non-null before going ahead and dereferencing them.

scohe001
  • 15,110
  • 2
  • 31
  • 51
1

The root class Node only has pointers to other Node's it does not know about what derived classes they might be.

It is "usually" best to have the correct interface in the base class to get results/values from the different kind of derived classes.

So for example if you have a base class animal:

class animal
{
    virtual int number_of_limbs() = 0;
}

Then the derived class pig:

class pig: public animal
{
   int number_of_limbs() override { return 3;}
}

By doing this the "interface" to the class is generic but each derived/specialisation can have it specific value.

In your case you probably just need a function called virtual int get_value() in your base class and then implement that in your ENode class...

(note all code above is pseudo code only)

code_fodder
  • 15,263
  • 17
  • 90
  • 167