Section 3 - Deriving a Float Class

7A.3.1 Building From a Base Class

Let's say we wish to create a useful list of floats from our generic list of "nothings."  As with the StackNodes and Stack data structure, we first create a new node called FloatNodeFloatNode will be derived from Node and therefore will not have to duplicate any data or linking methods.  It only has to add the float data to it.  That's what deriving classes is all about.

This process is beautifully simple.  Here is the FloatNode prototype:

// FloatNode prototype --------------------------------
class FloatNode : public Node
{
private:
  float data;
public:
  FloatNode(float x);
  void show();
  bool setData(float userData);
  float getData();
};

As you see, it is derived from Node.  It has only one member, the float data.  It has one constructor, the usual accessors, and it overrides the show() method of the base class.

// FloatNode method definitions --------------------------
FloatNode::FloatNode(float x) : data(x)
{
  // nothing else needed
}

void FloatNode::show()
{
  cout.setf(ios::fixed);
  cout.precision(3);
  cout << "[" << data << "] ";
}
bool FloatNode::setData(float userData)
{
  // no filtering needed here, but in most cases, there would be
  data = userData;
  return true;
}
float FloatNode::getData()
{
  return data;
}

7A.3.2 An Instant List of Floats

Guess what?  We don't have to derive any other classes to start using a float list.  We can use what we have immediately. Keep in mind this is still a little unrealistic because the client below uses FloatNodes rather than simple floats (the client shouldn't have to be bothered with the existence of such esoteric types as FloatNodes), but it will be a good test for your understanding of derived classes, pointer compatibility, destructors, etc.

In what follows, I assume the same multi-file project that we used with the StackNode and Stack examples.  In other words I added a header file List.h and a new source file List.cpp to the project and placed the prototypes for Node, List into List.h and their implementations into List.cpp.  You should do the same if you are reproducing this at home.

First have a look at the code in the main file and output:

#include <iostream>
#include <string>
using namespace std;
#include "List.h"

// FloatNode prototype --------------------------------
class FloatNode : public Node
{
private:
  float data;

public:
  FloatNode(float x);
  void show();
  bool setData(float userData);
  float getData();
};

// main method ---------------------------------------
int main()
{
  FloatNode *fp;
  Node *np;
  List myList;
  int k;

  // build a data-less list by inserting 10 nodes at front
  for (k = 0; k < 10; k++)
  {
    fp = new FloatNode(k);
    myList.insertAfterHead(fp);
  }
  myList.showList();

  // remove 5 nodes (and delete them!)
  for (k = 0; k < 5; k++)
  {
    np = myList.removeAfterHead();
    delete np;
  }
  myList.showList();

  // before leaving scope, clean up heap (relies on virtual destructor)
  while ( Node *np = myList.removeAfterHead() )
    delete np;

  return 0;
}

// FloatNode method definitions --------------------------
FloatNode::FloatNode(float x) : data(x)
{
  // nothing else needed
}

void FloatNode::show()
{
  cout.setf(ios::fixed);
  cout.precision(3);
  cout << "[" << data << "] ";
}

bool FloatNode::setData(float userData)
{
  // no filtering needed here, but in most cases, there would be
  data = userData;
  return true;
}

float FloatNode::getData()
{
  return data;
}

Output:

console shot

We are using a FloatNode pointer, fp, to create the nodes that go into the list because that is how we add actual numbers.  However, when we remove the nodes to delete them, we use a base Node pointer, np, since that is the return type of the removeAfterHead() method.

You should be enjoying the magic of inheritance and virtual functions now.  Without modifying the showList() method, our list of FloatNodes was printed to the screen.

Study this example before going on to the next section (which has even cooler things in it.)  Once you have made the code run, you can put the protected: modifier in the code thus reducing access to several of the methods to only derived classes.