Search code examples
c++classinheritancenodesedges

When base class wants to access member of an inherited class


this is my situation:

I am writing a data structure in C++ which consists of Nodes and Edges. Nodes are connected to other nodes by means of edges.

I have different types of nodes, such as text nodes, or int nodes. Text nodes are connected to text nodes and int nodes are connected to int nodes. I am using inheritance to implement the different types of nodes because it makes sense: fundamentally, all nodes are connected to other nodes so Node is the base class, and TxtNode, IntNode are inherited classes which share the fundamental property of a node.

However, this gives me problems when I try to fetch a connected inherited node from an inherited node, because the fetching function (function which retrieves a specific node connected to the calling node) is defined in the base class Node. For example, calling this fetch function from TextNode returns me the base version of the TextNode, which lacks extra information.

I am not sure what would be the sensible coding practice to do here. I chose to code it this way because it didn't seem to make sense that I must define a different connection fetch function for all different types of nodes.

Please let me know if there is any more information I can disclose.

Here is my code, I am trying to print an inherited node's partner but it will result in printing the base node.

Node.cpp

#include "Node.h"
#include "Edge.h"

#include "common.h"

#include <vector>
#include <cassert>
#include <cstddef>
#include <stdint.h>
#include <iostream>

Node::Node() {
    id = (intptr_t)this;
    edges.clear();
}

int Node::connect(Node* dst) {
    // establish link between calling node
    // and dst node, first linkage will
    // edge between each other, links after
    // increments strength. returns number of
    // links

    // fetch edge connecting this node and 
    // dst node
    Edge* edge = findDstEdge(dst);

    // if the branch isn't established yet,
    // then make connection
    if (edge==NULL) {
        establishConnection(dst, 1);
        return 0;
    } else {
        edge->strengthenConnection();
        return 1;
    }

}

Edge* Node::findDstEdge(Node* dst) {
    // fetches edge corresponding
    // to destination node

    // walk through vector of edges to find
    // edge connecting to dst

    vector<Edge*>::iterator iter = edges.begin();

while(iter!=edges.end()) {
    Edge* e = *iter;
    Node* partner = e->getPartner(this);
    if (partner->getID() == dst->getID())
        return e;
    iter++;
    }
    // not found
    return NULL;
}

void Node::establishConnection(Node* dst, int str) {
    // low level node addition

    // establish a new edge between
    // nodes which don't know each other
    Edge* e = new Edge(this, dst, str);
    this->manuallyConnect(e);
    dst->manuallyConnect(e);
}

void Node::manuallyConnect(Edge* e) {
    edges.push_back(e);
}

ostream& operator<<(ostream& stream,Node n) {

    vector<Edge*>::iterator iter = n.edges.begin();

    while(iter!=n.edges.end()) {
        Edge* e = *iter;
        stream << *e << endl;
        iter++;
    }

    return stream;
}

Node.h

#ifndef _NODE_H_
#define _NODE_H_

#include "common.h"

#include <vector>
#include <string>
#include <stdint.h>

class Edge;

using namespace std;

class Node {  

  protected:
    vector<Edge*> edges;
    intptr_t id;

  public:
    Node();

    // manipulation
    void establishConnection(Node* dst,
           int str);
    int connect(Node* dst);
    Edge* findDstEdge(Node* dst);

    // fetchers
    intptr_t getID() {return id;}
    vector<Edge*> getEdges() {return edges;}

    void manuallyConnect(Edge* e);

    friend ostream& operator<<(ostream& stream, Node n);
};

#endif

TxtNode.cpp

#include "TxtNode.h"
#include "Edge.h"

#include "common.h"

#include <iostream>

TxtNode::TxtNode(char c): Node()  {
    _c = c;
}

ostream& operator<<(ostream& stream, TxtNode tn) {
    // print out character of this TxtNode
    stream << "char: " << tn._c << endl;
    stream << "id: " << tn.id << endl;

    // print out all connections and its
    // strength
    vector<Edge*>::iterator iter = tn.edges.begin();

    while(iter!=tn.edges.end()) {
        Edge* e = *iter;
        stream << *e << endl;
        iter++;
    }

    return stream;
}

Edge.cpp

#include "Edge.h"
#include "Node.h"

#include "common.h"

#include <cassert>
#include <iostream>

using namespace std;

Edge::Edge(Node* a, Node* b, int str) {
    node_a = a;
    node_b = b;
    strength = str;
}

void Edge::strengthenConnection() {
    strength++;
}

Node* Edge::getPartner(Node* src) {
    uint src_ID = src->getID();
    if (node_a->getID() == src_ID)
        return node_b;
    else if (node_b->getID() == src_ID)
        return node_a;
    assert(false);
}

ostream& operator<<(ostream& stream, Edge e) {
    stream << "strength: "
           << e.strength
           << endl;
    stream << "node_a: "
           << e.node_a->getID()
           << endl;
    stream << "node_b: "
           << e.node_b->getID()
           << endl;
    return stream;
}

Currently I just have the code to print the ID which is an intptr_t, because I found out that I can't access inherited class's member from base class.

I am inclined to access the inherited class's member from base class because the edge class deals with base node class.


Solution

  • The classes code would've been a nice to have.

    From what you are telling you don't declare the fetch function virtual.

    Here's a very quick example on how virtual / non virtual functions work

    class baseNode {
     public:
        int fetch() { printf "fetched from base";};
    }
    
    class intNode : public baseNode {
     public:
        int fetch() { printf "fetched from derived int";};
    }
    
    class txtNode : public baseNode {
     public:
        int fetch() { printf "fetched from derived txt";};
    }
    

    The following code

    baseNode * ptr = new intNode();
    ptr->fetch();
    

    Will print "fetched from base"

    But if you declare the fetch function virtual :

    class baseNode {
     public:
         virtual int fetch() { printf " fetched from base";};
    }
    

    Then the same code will print "fetched from derived int".