Getters in recursive class with pointers to other instances of the class - c++

For a school project, I need to build a BinarySearchTree for a tuple of index and value of type T and I decided to make it using left and right child pointers (to other instances but bear with me).
To make the class a bit more realistic and readable, I built 3 different classes: Tree, BinaryTree, and BinarySearchTree with inheritance.
I declared the getter and the setter of the left and right children in the BinaryTree class and the class looks like this:
template <typename T>
class ArbreBinaire : public Arbre<T>
{
private:
ArbreBinaire<T>* _leftChild;
ArbreBinaire<T>* _rightChild;
public:
ArbreBinaire(lu index = 0, T element = T(0), ArbreBinaire<T>* lchild = nullptr, ArbreBinaire<T>* rchild = nullptr) :
Arbre<T>(index, element), _leftChild(lchild), _rightChild(rchild) {};
virtual ArbreBinaire<T>* getLC() const { return _leftChild; }
virtual ArbreBinaire<T>* getRC() const { return _rightChild;}
void setLC(ArbreBinaire<T>* lc) { _leftChild = lc; lc->setParent(this); }
void setRC(ArbreBinaire<T>* rc) { _rightChild = rc; rc->setParent(this); }
virtual ~ArbreBinaire() = default;
};
And in my BinarySearchTree, in a few places I need to go through my tree (to insert or search for exemple).
So here comes finally my question: What is the best way to get the left child of my object, which is of type BinarySearchTree and to keep the continuity (BST that has BST left child and BST right child)
I thought of multiple options:
virtual getter and setter and redefining it in every derived class but there are 2 other "problems" with that solution.
Should I have 2 other members in my BST, which would be of type BST* and basically point to the same object than the ones in my BinaryTree ?
Should I just redefine my getters to return a dynamic_cast version like this : BST<T>* getLC() { return dynamic_cast<BST<T>*> (this->BinaryTree::getLC()) } ?
Putting all my members, getters and setters in my BST class (but that goes opposite to the definition of a BinaryTree, the base class : a BinaryTree with no children is not a BinaryTree)
3.Just casting every time i need to get my left or right child
4.Casting everytime i need to use a method in BST (derived class) ?
5. Any other suggestions?

Related

Implement a virtual function for two derived classes, that is the same except for one variable Type

I have an abstract class Node that can either be a Leaf or a NonLeaf. I have written a large function SplitNode. The problem is, this function is basically the same for a Leaf as for a NonLeaf. The only difference being that it operates on the entries vector for Leafs, as opposed to the children vector, for NonLeafs. The code is otherwise identical in both cases. For example in one case I do entries[i]->r to access some Rectangle property, and in the other case I do children[i]->r. So the main difference beyond the 2 variable names, is the type of the actual vector. How am I supposed to implement this, without copying and pasting the same function, implemented slightly differently for Leaf and NonLeaf?
Edit: I also want the SplitNode function to be able to be called recursively.
class Leaf;
class Node
{
public:
Node();
virtual Leaf& ChooseLeaf(const Rectangle& entry_r) = 0; // this makes the Node class Abstract
Rectangle r;
unique_ptr<Node> parent;
};
class Leaf : public Node
{
public:
Leaf();
Leaf& ChooseLeaf(const Rectangle& entry_r) override;
vector<unique_ptr<IndexEntry>> entries;
};
class NonLeaf : public Node
{
public:
NonLeaf();
Leaf& ChooseLeaf(const Rectangle& entry_r) override;
vector<unique_ptr<Node>> children;
};
Dummy illustration of the SplitNode() function:
void SplitNode()
{
// in the Leaf case:
if (this.entries.size() > rtree.M)
{ ... }
// in the NonLeaf case:
if (children.size() > rtree.M)
{ ... }
// in the Leaf case:
entries[0]->r.DoSomething();
// in the NonLeaf case:
children[0]->r.DoSomething();
// Recursion
parent.SplitNode();
...
|
This is a textbook case for a template function. Presuming that the common logic freestanding logic whose only dependency is the vector itself:
template<typename T>
void doSplitNode(T &entries_or_children)
{
for (auto &entry_or_child:entries_or_children)
{
auto &the_r=entry_or_child->the_r;
// Here's your entries[i]->r, or children[i]->r
}
}
// ...
class Leaf : public Node
{
public:
// ...
void SplitNode()
{
doSplitNode(entries);
}
};
class NonLeaf : public Node
{
// ...
void SplitNode()
{
doSplitNode(children);
}
};
Additional work will be needed of the shared logic has additional dependencies. There's no universal solution here, everything depends on the details. Perhaps the template itself can be moved into a class, with both NonLeaf and Leaf multiply-inheriting from it, and then implementing the additional dependencies as virtual/abstract methods.

Derived class constructor calling base class constructor

My question is whether or not it's ok call the base class constructor as being initialized to nullptr the way I have below in derivedClassA. The code works as expected but it still feels wrong.
For the sake of length I have omitted other classes that are derived from class Tree , but the idea here is that you can create a Tree object that has any number of derived classes, and call getStringExpression() and it will return whatever getStringExpression evaluates to. For example, if an object of type Tree contains a pointer to an object of DerivedClassA, which has right and left children equal to "Car" and "Ride" then Tree.getStringExpression(); would return "(Car + Ride)". Assume the values Car and Ride are returned by concrete classes derived from Tree that return the string "Car" and "Ride" respectively when their getStringExpression() method is called (and they have no children).
It feels wrong to initialize the base class constructor to nullptr the way I have in derivedClassA because if I create a single object of derivedClassA, then this object is the root node with its two children created during its construction. What happens to the constructed Tree in this scenario? Since it's pointer is nullptr it's not really being used or it's sort of disconnected or univolved with the object of derivedClassA...
I apologize in advance because I know this isn't the most straightforward question.
Tree.h-
class Tree
{
public:
explicit Tree(Tree *rootNode);
virtual ~Tree() {};
virtual std::string getStringExpression() const;
private:
Tree *rootNodePtr = nullptr;
};
Tree.cpp-
#include "Tree.h"
Tree::Tree(Tree *rootNode)
{
rootNodePtr = rootNode;
}
std::string Tree::getStringExpression() const
{
if(rootNodePtr != nullptr)
{
return rootNodePtr->getStringExpression();
}
return "Tree has no children";
}
derivedClassA.h-
#include "Tree.h"
class derivedClassA :
public Tree
{
public:
derivedClassA(Tree *leftChild, Tree *rightChild);
virtual ~derivedClassA();
virtual std::string getStringExpression() const override;
private:
Tree *leftChildPtr = nullptr;
Tree *rightChildPtr = nullptr;
};
derivedClassA.cpp-
#include "derivedClassA.h"
derivedClassA::derivedClassA(Tree *leftChild, Tree *rightChild): Tree(nullptr)
{
leftChildPtr = leftChild;
rightChildPtr = rightChild;
}
derivedClassA::~derivedClassA()
{
}
std::string derivedClassA::getStringExpression() const
{
auto expressionValue = "(" + leftChildPtr->getStringExpression() + "+" + rightChildPtr->getStringExpression() + ")";
return expressionValue;
}
It sounds like you have merged the classes of Tree and Node. You are initializing Tree in the constructor with a single "root" tree, when it should have a "root Node that itself has two children. In that case, Node wouldn't inherit from Tree - you would have two separate classes.
Or perhaps you have an abstract Node class and a BinaryNode that inherits it to represnet a node with two children, opening up the door for different node types (nodes with more than two children, or unbalanced children).
So to answer your question, yes it seems odd that you are initializing the "base" with a default value, but I think it's because your class hierarchy is wrong. Meaning derivedClassA is not a Tree.

Inheritance and AVL/BST Trees

Is there any way to use the same insert function for both Bst and Avl tree? The problem is that Bst and Avl have different Node types, but I don't want to make the Bst Node a general case(with height and Node* parent inside, which makes no sense because there is no need of parent and height inside a Bst).
class Bst
{
public:
struct Node
{
int value;
Node* left;
Node* right;
};
Node* insert(Node* node) {/* do stuff using Bst::Node */}
// ...
};
class Avl : public Bst
{
public:
struct Node : public Bst::Node
{
int height;
Node* parent;
};
// now I want that Bst::insert use this Node
// instead of the old one
Node* insert(Node* node)
{
Node* inserted_node = Bst::insert(node);
/* rotations stuff */
return inserted_node;
}
};
Roughly what I'm trying to do is make Bst::Node "virtual".
So, how can I solve the problem of implenting the Avl Tree without rewriting the entire insert function just because Node changed?
Actually I'm also working on this stuff and I think you're very clear to describe what you want.
At the first, it's may be little confuse about the given interface, insert() should not return the pointer of the Node, doesn't it. We may use the findNode() function, which return the pointer of the Node and exactly do this work only.
Back to the main question, may be you can use the template to set your Node type for every function in the BST.
But the BST is not just a abstract interface, which also implement the BST operation, so it's not CRTP..
The pseudo code for now may be the following :
// pre-define :
//parent ptr also alleviate the implementation of BST.
template<typename T>
class BST{
... omit..
protected:
template<typename node_type>
class BST_Node{
public:
T val;
BST_Node *left, *right, *parent;
BST_Node():left{nullptr},
right{nullptr},
parent{nullptr}, val{}{};
// empty {} default to value initialization.
}
... omit ...
}
template<typename T>
class AVL_Node : public BST_Node{
public:
short height;
AVL_Node(T val):BST_Node(val), height(0){};
}
template<typename T>
void insert(T val){
AVL_Node<T> Node(val);
BST<T>::insert_node<AVL_Node>(Node);
AVL_Node<T>* ptr = BST<T>::find_node<AVL_Node>(val);
ptr->height = BST<T>::get_height(ptr);
state = chk_balance(ptr);
switch(state){
case 0: // tree very balance..
break;
case 1:
LL_rotate(ptr);
break;
case 2:
RR_rotate(ptr);
break;
... omit
}
}
# help this post solve your question..
Maybe you want CRTP (in which case you haven't given enough info about your needs for even a rough example, but a simpler less powerful template approach may make more sense to you. Have a base class (under each of your tree types) that has no data members, and just defines static template functions for the common code. Since the functions are static, you need to pass in the relevant data (for insert that should be &root) but that should not be much trouble. (Rough and untested):
struct tree_base
{
template <class Node>
static Node* insert( Node** where, Node* what)
{
Node* here;
while ( (here = *where) != 0 )
{
if ( *what < *here ) where = &(here->left);
else if ( *here < *what ) where = &(here->right);
else
{
Trying to insert something already there, what should be done
}
}
*where = what;
return what; // Is that the desired return?
}
};
Then each of your real tree classes would inherit from tree_base and would call tree_base::insert(&root, new_node) to do the common parts of insert
A CRTP version of that would allow root to be a member of the base class even though it points to the Node type of the derived class. Given root as a member of the base class, the insert function doesn't need to be static and doesn't need to take &root as input. And since a CRTP base class is already correctly templated to have access to the Node type, the base class insert method wouldn't need to be a template. All that would be a lot more things to learn (by looking at some real examples of CRTP) and probably overkill for the code sharing you want.

Remove dynamic_cast in derived class of tree node

I have a class which is a node of a tree, called Node. I need to create a DerivedNode class type which has some extra functionality. The problem is that Node has a vector of Node* as a member variable, so when DerivedNode inherits from Node, it inherits this vector. I've created a basic example showing the issue:
#include <iostream>
#include <vector>
class Node {
public:
Node(int value_) : value(value_) {}
int foo() { return value; }
virtual void add(Node* new_node) {
children.push_back(new_node);
}
protected:
std::vector<Node*> children;
int value;
};
class DerivedNode : public Node {
public:
DerivedNode(int value_) : Node(value_) {}
int bar() { return value*2; }
// Ensures we only add children of type DynamicNode*
virtual void add(DerivedNode* new_node) {
children.push_back(new_node);
}
void print() {
for (size_t i = 0; i < children.size(); ++i) {
std::cout << dynamic_cast<DerivedNode*>(children[i])->bar() << std::endl;
}
}
};
int main() {
DerivedNode* child_a = new DerivedNode(5);
DerivedNode* child_b = new DerivedNode(6);
DerivedNode parent(1);
parent.add(child_a);
parent.add(child_b);
parent.print();
delete child_a;
delete child_b;
}
My question is, how can I do this without the dynamic_cast? My actual code is far more complex which means that there are dynamic casts everywhere.
First add function in derived class is totally useless, it does not override add function from base class, it overloads it. In such way you still can add Node*'s to the derived class. To prevent this you should override add(Node*) as private.
If you does not like dynamic cast, you may use static cast instead
or
You may have virtual bar in base class that does not do anything
or
you can cast the vector itself (the whole thing) and assign to reference or pointer to std::vector DerivedNode*
You can't have it both ways. You either have IS-A principle reflected in the design of your classess, or you don't. If DerivedNode is Node, than the vector of Nodes should be indistinguishable from vector of DerivedNodes - and no casts are neccessary. If this can not be achieved, that you simply can not use vector of base pointers.
Any dynamic_cast in production code for me is a hard block for any review, as it clearly violates the basic design principles.

c++ handling derived class that's self referencing

So suppose I have a tree class like this in c++
class Node{
void addChild(Node*);
/*obvious stuff*/
protected:
Node* parent;
vector<Node*> children
}
class specialNode : public Node{
void addChild(specialNode*);
/*obvious stuff*/
/*special stuff*/
}
Now whenever I access the children in specialTree, I obviously get Node*, not specialNode*.
But this specialNode* has member variables and functions that Node doesn't have.
I can force specialNode to only have take specialNode as children and otherwise break in compile time,
but I still get Node* when accessing children/parent, and I have to cast it whenever I want to use special functions, even in specialNode functions.
Is there any clever, or just any better way to go about this?
Other than literally casting every time?
If you only need SpecialNode objects in your tree (and just want to encapsulate all generic tree functionality in Node) you can make Node a so called "mix-in" class like
template <class N>
class Node : public N {
public:
void addChild(Node<N>*);
protected:
Node<N>* parent;
vector<Node<N>*> children;
};
class SpecialNodeBase {
// Here comes all "special" data/methods for your "special" tree
};
typedef Node<SpecialNodeBase> SpecialNode;
After that you can construct a tree of SpecialNode objects and use all methods from SpecialNodeBase as well as additional tree-managing functions from Node
Because addChild function in your child class is not polymorphism, make it virtual, but overloading functions across base/child members is not allowed, so we have to change the addChild parameter in the child class:
class Node{
virtual void addChild(Node*);
...
}
class specialNode : public Node{
virtual void addChild(Node*);
...
}
Now, it should work.
If you want to access to the childeren variable from the child class (specialNode class), you should cast it. For example:
specialNode* var = static_cast<specialNode*>(children[i]);
Since we declared addChild as a virtual function, then we should use dynamic_cast instead of static_cast if we aren't sure that children[i] is always an instance of specialNode class, and thus it is better to use dynamic_cast:
specialNode* var = dynamic_cast<specialNode*>(children[i]);
if(var != NULL)
{
//...
}
If I understand correctly, the "Mix-in" class solution won't allow you to call addChild from functions implemented by SpecialNodeBaseClass.
You can actually do the following:
template <class recursiveT>
class Base {
public:
Base(dataType data) { populate children with data; }
void addChild() { something base class appropriate; }
protected:
std::vector<recursiveT> children;
};
class Derived: public Base<Derived> {
public:
/* note: the constructor here will actually call the
constuctor of the base class */
Derived(dataType data) : Base<Derived>(data) {}
/* other special functions go here. */
};
This may look a little crazy, but it compiles cleanly for me on several GCC versions so I'm inclined to believe it's not totally wrong-headed. You should now be able to call the functions of Base from inside Derived.
You will definitely have to cast the Node * to a specialNode * at some point, but you can make this clean and easy to manage by doing this in only one place. You could add a member function, say getParent and override it in specialNode, like this:
class Node {
...
virtual Node *getParent() {
return parent;
}
};
class specialNode : public Node {
...
specialNode *getParent() {
return dynamic_cast<specialNode *>(parent);
}
};
Of course, this is assuming that specialNodes always have other specialNodes as parent/children. If you mix Nodes and specialNodes, this obviously won't work.