Container implementation using templates and inheritance - c++

I'm trying to implement a generic ranked tree container by inheriting a basic 2-3 tree container
basic tree is declared as follows :
template<typename T>
class Node{
// etc..
}
template<typename T>
class Tree{
Node* root;
// etc
}
and suppose i wanted to implement a tree of integers, i though i could declare a class as follows :
class RankedNode : public Node<int>{
// ...
}
class RankedTree : public Tree<int>{
RankedNode root*; // for example
// ...
}
The class RankedNode will include additional fields to the class Node, that aid in fast traversal of the tree ( O(log n) ) , and by using inheritance i'll be able to prevent code duplication.
However, i don't know how to go about doing so correctly, any ideas would be appreciated :)

You could start by making Tree a template on N, (the node type), instead of T (the value type):
template<typename T>
class Node{
// etc..
}
template<typename N>
class Tree{
N* root;
// etc
}
This way you could write
class RankedNode : public Node<int>{
// ...
}
class RankedTree : public Tree<RankedNode>{
// RankedNode* root; <- remove this; it is already in Tree<RankedNode>
// ...
}
and add only fields that are really new compared to base classes.

Related

Inheriting from a class but using a subclass of an attribute

I have to implement a Red/Black tree (RBTree) in C++, inheriting from a Binary Search Tree (BSTree) class I already created.
The BSTree class contains a Node pointer (Node is a class I created for BSTree) to the tree root.
I want to create a Node subclass called "RBTNode", which contains color attribute and related methods (so RBTNode is subclass of Node), then I want to create the RBTree class inheriting from BSTree, but with the new RBTNode instead of the standard Node.
The structure would be as follows:
BSTree contains Node;
RBTNode is subclass of Node;
RBTree is subclass of BSTree;
RBTree contains RBTNode;
How could I achieve that?
As suggested by Jarod42 in the comments, you can achieve this by making the node type a template parameter for the BSTree class.
Say you have these two node classes:
template<typename KeyT>
class BSNode {
public:
KeyT key;
// ...
};
template<typename KeyT>
class RBNode : public BSNode<KeyT> {
public:
bool color;
};
Then you could add a node type template parameter to BSTree and make it BSNode by default. You can then use RBNode while inheriting in RBTree.
template <typename KeyT, typename NodeT = BSNode<KeyT>>
class BSTree {
protected:
NodeT *root;
};
template <typename KeyT>
class RBTree : public BSTree<KeyT, RBNode<KeyT>> {
// root will be of type RBNode<KeyT>* here
};

How to structure the inheritance from generic tree to a-b tree

I am trying to implement an a-b tree, as a derived class from a generic tree.
The generic tree node is as follows:
template<typename T>
struct TreeNode
{
T value;
std::vector<TreeNode*> children;
//Some other trivial stuff
};
The structure of the a-b node is as follows:
template<typename T>
struct ABTreeNode : TreeNode<T>
{
std::vector<T> keys;
//The idea is to omit the T value field of the base node and use that vector for the keys
};
Also in the generic tree class there exists a root field
TreeNode *root;
And the a-b constructor is
template<Typename T>
ABTree<T>::ABTree(T value)
{
GenericTree<T>::root = new ABTreeNode;
root->keys.push_back(value);
}
Now, the way this is made, I need to use down casting in a lot of the a-b tree methods, for example:
template<typename T>
bool ABTree<T>::search(T value)
{
ABTreeNode *node = GenericTree<T>::root;
//....
}//Downcast base to derived
As far as I know down casting is a bad practice and indicates bad design. The fact that I use variables defined in the derived struct but declare the node as base struct seems very error prone. What would happen if that node was created as a base node and not derived?
Eg:
//Somewhere:
TreeNode *node = new TreeNode;//Instead of new ABTreeNode
//..
//Somewhere else
node->keys//Shouldn't that be an error?
Is my approach correct? If not how should I structure it better?
PS: spare the raw pointers please.
Sharing code by inheritance is a bad design. Better is to use Composition - see https://en.wikipedia.org/wiki/Composition_over_inheritance
To share code between different implementations of various trees I would extract common fields into a struct.
template <class T, class ChildT>
struct TreeNodeCommons
{
T nodeValue;
std::vector<ChildT*> children;
// more common fields
}
Then I would attach it to Nodes of different types.
template<typename T>
struct ABTreeNode
{
TreeNodeCommons<T, ABTreeNode<T>> commons;
std::vector<T> keys;
};
You may then write templated algorithms assuming Node contains field named commons and you may write Node specific algorithms as well. And there is no dynamic_casts.

Does this class setup violate the Liskov Substitution Principle

I have this piece of code for a Tree. The BSTnodes contain the actual data. BST is a wrapper around them by inheriting from unique_ptr<BSTnode<Key,Data>>. BST doesn't add any new fields to the class.
The inheritance makes it so that my tree is a unique_ptr<BSTnode>, but is that a correct way of implementing it? The added operations for the BST like rotate() / insert() or remove() are specific to the data structure. You wouldn't and shouldn't expect them for a regular unique_ptr, but this does mean that a BST can't be used interchangeably with a unique_ptr.
If this implementation strategy is incorrect, how should I solve it?
template <class Key, class Data>
class BST : public unique_ptr<BSTnode<Key, Data>>
{
using unique_ptr<BSTnode<Key, Data>>::unique_ptr;
// operations ...
};
template <class Key, class Data>
class BSTnode
{
friend class BST<Key, Data>;
public:
//constructors ...
protected:
Key key;
Data data;
BSTnode<Key, Data> *parent;
BST<Key, Data> left, right;
};
LSP aside, inheriting standard classes is generally problematic and not a recommended solution for most cases. In this case, as #SomeProgrammerDude suggests, it's better to use composition and put the pointer inside your class:
template <class Key, class Data>
class BST
{
std::unique_ptr<BSTnode<Key, Data>> root;
// operations ...
};
Noone would want to use your BST class to replace the unique_ptr anyway. It's a separate data container that just happens to utilize unique_ptr to store its data.

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.

C++ Defining a specific object type when extending a template class

I have written a short template list class defining some methods I would like to use in implementations of it. My current problem is that I am having trouble extending the generic template type and defining a specific type.
In other words, assume I have a class called MyType
List.h:
template<class T>
class List
{
public:
Node<T> *head;
Node<T> *tail;
...
}
Inside the Node class:
public:
virtual T getData();
I want to extend this class with a specific defined type MyType so that I can have getData() behave differently in the extended class MyTypeList. I am having trouble with syntax. Any help would be greatly appreciated!
class MyTypeList : public List<T>
{
...
}
You can do something like this:
struct MyTypeList : List<MyType> {
};
although it is about the same as doing:
typedef List<MyType> MyTypeList;