1

I am trying to represent an abstract syntax tree in C++. I have multiple classes which inherit base class 'TreeNode'. Some of them contain pointers to other inherited classes of TreeNode within them. With the method i am trying to use to get it to work, I can't seem to access the members within the child TreeNodes. Am i doing something wrong? Is there a better way to achieve this?

class TreeNode {};

class UnaryOperation : virtual public TreeNode {
public:
    TreeNodeType Type;
    UnaryOperationType Operation;
    TreeNode* Operand;
    UnaryOperation(UnaryOperationType operation, TreeNode* operand);
};

class BinaryOperation : virtual public TreeNode {
public:
    TreeNodeType Type;
    TreeNode* Left;
    BinaryOperationType Operation;
    TreeNode* Right;
    BinaryOperation(TreeNode* left, BinaryOperationType operation, TreeNode* right);
};

. . .

TreeNode* unaryop_node = new UnaryOperation(op_type, operand_node);
return unaryop_node;

...

std::cout << (int)*unaryop->Type << std::endl;

class TreeNode has no member Type

Mezza Man
  • 63
  • 1
  • 2
  • 8
  • 1
    Well, the compiler is right - `TreeNode` doesn't have such a member. Why don't you move `Type` to the base class, since it applies to all your nodes? – molbdnilo Jul 01 '20 at 10:40
  • Yeah i guess i could. But because it is a UnaryOperation, would it not have member `Type`? – Mezza Man Jul 01 '20 at 10:44
  • `UnaryOperation` has the member `Type`, but `unaryop_node` is a (pointer to) `TreeNode`, which does not have any `Type`. – Mikael H Jul 01 '20 at 10:48
  • Does this answer your question? [C++ Access derived class member from base class pointer](https://stackoverflow.com/questions/2436125/c-access-derived-class-member-from-base-class-pointer) – codeling Jul 01 '20 at 10:48
  • How can i make `Left` and `Right` for example in the BinaryOperation class accept any of the inherited classes? – Mezza Man Jul 01 '20 at 10:49
  • Left and Right are TreeNode's, so they *do* "accept any of the inherited classes"... if you're trying to access a "Type" member AND want that to be part of any TreeNode, then, well, it just HAS to be part of the TreeNode class. That's how inheritance / access to members / access via base classes works in C++ - what you're trying to do is "duck typing", which isn't supported by C++ (do you maybe come from a language where this exists?) – codeling Jul 01 '20 at 10:51
  • Yeah. I am quite new to c++, and i don't understand how you would represent a tree structure with different node classes with inheritance. Is there some other way to do this? – Mezza Man Jul 01 '20 at 10:56
  • @MezzaMan, is `TreeNodeType` unique per sub type? (i.e., will all `UnaryOperation`s have the same `Type`?) – Mikael H Jul 01 '20 at 11:10
  • Yeah. Its just to check which type they are easily – Mezza Man Jul 01 '20 at 11:40

2 Answers2

1

Thanks for all your answers. It turns out I was trying to solve this problem in the wrong way. I completely removed the TreeNodeType enum, and instead used a virtual function to traverse the tree.

class TreeNode {
public:
    TreeNode();
    virtual void print() = 0;
};

class UnaryOperation : public TreeNode {
public:
    UnaryOperationType Operation;
    TreeNode* Operand;
    UnaryOperation(UnaryOperationType operation, TreeNode* operand);
    void print() override {
        print_unaryop(Operation);
        Operand->print();
    }
};

class BinaryOperation : public TreeNode {
public:
    TreeNode* Left;
    BinaryOperationType Operation;
    TreeNode* Right;
    BinaryOperation(TreeNode* left, BinaryOperationType operation, TreeNode* right);
    void print() override {
        Left->print();
        print_binaryop(Operation);
        Right->print();
    }
};```

`unaryop_node->print();`
Mezza Man
  • 63
  • 1
  • 2
  • 8
0

As a general note, in C++, when you have a pointer to a certain type, you can only access members/functions that this specific type has; you don't have access to any of the members/functions in derived classes (directly); this works only e.g. via casting or virtual functions.

From the way you're trying to access the created UnaryOperation via a TreeNode pointer, it looks like Type should be part of the base class TreeNode, something like:

class TreeNode {
public:
    TreeNodeType Type;
};

class UnaryOperation : virtual public TreeNode {
public:
    UnaryOperationType Operation;
    TreeNode* Operand;
    UnaryOperation(UnaryOperationType operation, TreeNode* operand);
};

class BinaryOperation : virtual public TreeNode {
public:
    TreeNode* Left;
    BinaryOperationType Operation;
    TreeNode* Right;
    BinaryOperation(TreeNode* left, BinaryOperationType operation, TreeNode* right);
};

This assumes that TreeNodeType indicates the type of node and will for example be the same for all instances of type UnaryOperation; it could be defined something like

enum TreeNodeType { UnaryOp, BinaryOp };

Note that requiring such a "type indicator" in a base class in C++ is typically a code smell that you are not doing inheritance the way it's supposed to be done in C++ - typically you either should not need to differentiate between different derived types contained in a TreeNode (because you call virtual functions that automatically do what is required for the specific subtype contained in the generic base pointer), or you should have a pointer to the specific class.

codeling
  • 11,056
  • 4
  • 42
  • 71