How do I implement polymorphism in a nested class?

518 views Asked by At

EDIT: specific example at the bottom

The best way to explain the problem is by example (note that solutions for this specific example aren't the answers I'm looking for).

Say I have a base class Base, which is basically a container:

class Base {
protected:
    class BaseNode {
    protected:
        BaseNode* next;
        int id;
    };
    Node* head;
public:
// ... functionality
};

And now I want to derive and improve it, adding an extra data field:

class Derived : public Base {
private:
    class DerivedNode: public BaseNode {
        char* name;
    };
public:
// .. some more functionality
};

What I want is a Derived class with all the functionality of Base, but have every node also include a name, not just an id.

But if I look at an instance of DerivedNode, what I want would be:

DerivedNode = {
    DerivedNode* next;
    int id;
    char* name;
};

But what I have is:

DerivedNode = {
    BaseNode* next;
    int id;
    char* name;
};

So DerivedNode()->next->name would be undefined!

Not to mention that the Base class only handles BaseNodes... all calls to new and delete wouldn't allocate enough memory for the char* in DerivedNode.

This seems like a very generic problem, is there some neat way of solving this? Most of Bases functionality would be really useful even for DerivedNodes, so I would prefer to keep the inheritance scheme as it is...

Edit: A specific example for when this may be useful

I don't really want to put lots of code, so I'll try to explain... I've asked a question regarding my specific needs here if anyone wants to see an example.

I'm implementing a List class. That is my base class. It is templated (the data in each node is templated, and each node also has *next and *prev fields), has an iterator nested class, insertion, removal by iterator location... the works.

I want to implement a Forrest class, which is a list of data node (still templated), but each node includes a list of TreeNodes that point to the node. I want to do this because the data is sortable and I want to be able to fetch it in log(n) time (hence, a tree), but it is sortable in two different ways (say, age and ID number), so I need two tree pointing to the same data.

So: I made Forrest inherit from List, and derived ForrestNode to include a list of TreeNodes that point to the data:

class DerivedNode: public List<T>::ListNode {
    List<Tree<T*>::TreeNode*> treeNodes;
};

The base class List doesn't need to know about the new treeNodes field, because it is irrelevant to the lists functionality - methods like swap, get_head, is_empty should work the same, and creating a new node should work pretty much the same (need to allocate more memory, but the new treeNodes field should never be initialized with data).

The new Forrest class, however, will override the base Lists methods, but only with added functionality - for example, after insertion (regular list insertion), a pointer will be inserted into the relevant tree, and then the address of the new tree node will be added to the treeNodes list.

2

There are 2 answers

4
Kristian Duske On BEST ANSWER

You can simply turn your Base::BaseNode into a template and provide the type of the next member variable as the type argument, like so:

class Base {
protected:
    template <class Derived>
    class BaseNode {
    protected:
        Derived* next;
        int id;
    };
    Node* head;
public:
// ... functionality
};

class Derived : public Base {
private:
    class DerivedNode: public BaseNode<DerivedNode> {
        char* name;
    };
public:
// .. some more functionality
};
2
Nik On

As mentioned by @stefaanv we can use templates to achieve what you want. Please verify below program:

#include <iostream>

using namespace std;


class Base {

protected:

    template<typename Z>
    class BaseNode{

    protected:

        Z* next;
        int id;
    };

public:

};


class DerivedNode;


class Derived : public Base{

private:

    class DerivedNode: public BaseNode<DerivedNode>{

        public:
        const char* name;

        void setId(int val){id = val;}
        void setNext(DerivedNode* nx){next = nx;}
        DerivedNode* getNext(){return next;}


    };

    DerivedNode* derHead;

public:


  Derived()
  {
      DerivedNode* node1 = new DerivedNode();

      node1->name = "Nik";
      node1->setId(1);
      node1->setNext(NULL);



      DerivedNode* node2 = new DerivedNode();

      node2->name = "Aka";
      node2->setId(2);
      node2->setNext(NULL);

      node1->setNext(node2);

      derHead = node1;
  }
  DerivedNode* getHead(){return derHead;}

  void print()
    {
       DerivedNode* tmp = derHead;
       while(tmp != NULL)
       {
           cout<<tmp->name<<endl;
           tmp = tmp->getNext();
       }

    }
};



int main()
{
   Derived obj;
   obj.print();

   return 0;
}