20-CS-122-001 Computer Science II Spring 2012
Virtual Functions: Example

Virtual functions, classes, inheritance, lists, queues, stacks, applications

Source


// Virtual functions are used to build a LinkedList class which allows
// any mix of types of Object subclasses to be stored and which allows
// finding stored objects using a user-built key.  Three example Object
// subclasses include a simple string class (Person), a simple integer
// class (Number), and even the LinkedList class itself.
#include <string.h>
#include <iostream>
using namespace std;

#define PERSON 1
#define NUMBER 2
#define LIST 3

// Identifiers
class Identifier {
   int type;
public:
   void *pattern;
   Identifier (void *i, int t) { pattern = i; type = t; }
   int typeOf() { return type; }
};

// Object Class:
//   Subclassed by Person, Number, and LinkedList Classes
class Object {
protected:
   int type;
public:
   virtual bool matchIdent(Identifier *identifier) { return false; }
   virtual void identify(ostream & out) {};
   virtual bool equals(Object *obj) { return false; };
   int typeOf() { return type; }
};

ostream &operator<<(ostream &out, Object &o) { o.identify(out); return out; }

ostream &operator<<(ostream &out, Object *o) { o->identify(out); return out; }

// Person Class: just saves the name of a person
class Person : public Object {
   char *ident;

public:
   Person (char *s) {
      ident = new char[strlen(s)+1];
      strcpy(ident,s);
      type = PERSON;
   }

   bool matchIdent(Identifier *identifier) {
      if (identifier->typeOf() != PERSON) return false;
      return !strncmp((char *)identifier->pattern, ident, strlen(ident));
   }

   bool equals(Object *obj) {
      return !strncmp(((Person *)obj)->ident,ident,strlen(ident));
   }

   void identify (ostream & out) {
      out << "[" << ident << " (Person)] ";
   }
};

class PersonIdentifier : public Identifier {
public:
   PersonIdentifier (char *name) : Identifier (name, PERSON) {}
};

// Number Class: just saves a number
class Number : public Object {
   int number;

public:
   Number (int n) {  number = n;  type = NUMBER; }

   bool matchIdent(Identifier *identifier) { 
      if (identifier->typeOf() != NUMBER) return false;
      return *(int *)identifier->pattern == number; 
   }

   bool equals (Object *obj) {
      return number == ((Number *)obj)->number;
   }

   void identify (ostream & out) {
      out << "[" << number << " (Number)] ";
   }
};

class NumberIdentifier : public Identifier {
public:
   NumberIdentifier (int n) : Identifier (new int(n), NUMBER) {}
};

class Cell {
friend class LinkedList;
friend ostream & operator << (ostream & out, LinkedList & lst);

   Object *object; Cell *next;
   Cell (Object *obj, Cell *nxt) {  object = obj;  next = nxt;  }
};

// LinkedList Class:
//   insert (Object *obj) - insert obj at the head of the list.
//   locate (Identifier *identifier) - finds an object in the
//       list that matches the information contained in identifier.
//   matchIdent (Identifier *identifier) - return true iff this
//       object matches the information of the identifier.
//   identify (ostream &out) - sends information to the stream out.
// both matchIdent and identity are developed because the LinkedList
// class subclasses the Object class.
class LinkedList : public Object {
friend ostream & operator << (ostream & out, LinkedList & lst);

   Cell *head;

public:
   LinkedList () { head = NULL; type = LIST; }

   void insert (Object *obj) {  head = new Cell(obj, head);  }

   Object *locate(Identifier *identifier) {
      for (Cell *ptr=head ; ptr != NULL ; ptr=ptr->next) 
	 if (ptr->object->matchIdent(identifier)) return ptr->object;
      return NULL;
   }

   bool matchIdent (Identifier *identifier) {
      if (identifier->typeOf() != LIST) return false;
      LinkedList *ll = (LinkedList *)identifier->pattern;
      return equals(ll);
   }

   bool equals (Object *obj) {
      Cell *ptr2 = ((LinkedList *)obj)->head;
      Cell *ptr = head;
      for ( ; ptr2 != NULL && ptr != NULL ; ptr2=ptr2->next, ptr=ptr->next)
	 if (!(ptr2->object->equals(ptr->object))) return false;
      if (ptr == NULL && ptr2 == NULL) return true;
      return false;
   }

   void identify (ostream & out) {  out << *this;  }
};

ostream & operator << (ostream & out, LinkedList & lst) {
   out << "{ ";
   for (Cell *ptr=lst.head ; ptr != NULL ; ptr=ptr->next) 
      out << *ptr->object;
   out << "} ";
   return out;
}

class ListIdentifier : public Identifier {
public:
   ListIdentifier (LinkedList & ll) : Identifier (&ll, LIST) {}
};

int main() {
   Object *obj, *ll;
   LinkedList lst, ts1, ts2;

   lst.insert(new Person("John"));
   lst.insert(new Number(100));
   lst.insert(new Person("Jim"));
   lst.insert(new Number(234));
   cout << lst << "\n\n";
   if ((obj = lst.locate(new PersonIdentifier("John"))) != NULL)
      cout << "Found: " << obj << "\n";
   if ((obj = lst.locate(new NumberIdentifier(100))) != NULL)
      cout << "Found: " << obj << "\n";
   if ((obj = lst.locate(new NumberIdentifier(234))) != NULL)
      cout << "Found: " << obj << "\n";
   if ((obj = lst.locate(new PersonIdentifier("Jim"))) != NULL)
      cout << "Found: " << obj << "\n\n";

   ts1.insert(new Person("James"));
   ts1.insert(new Number(654));
   ts1.insert(new Person("Jerry"));
   cout << ts1 << "\n\n";
   lst.insert(&ts1);
   lst.insert(new Person("Jake"));
   cout << lst << "\n";

   ts2.insert(new Person("James"));
   ts2.insert(new Number(654));
   ts2.insert(new Person("Jerry"));
   if ((ll = lst.locate(new ListIdentifier(ts2))) != NULL)
      cout << "\nFound: " << ll << "\n"; 
}

// Sample Output
{ [234 (Number)] [Jim (Person)] [100 (Number)] [John (Person)] } 

Found: [John (Person)] 
Found: [100 (Number)] 
Found: [234 (Number)] 
Found: [Jim (Person)] 

{ [Jerry (Person)] [654 (Number)] [James (Person)] } 

{ [Jake (Person)] { [Jerry (Person)] [654 (Number)] [James (Person)] } [234 (Number)] [Jim (Person)] [100 (Number)] [John (Person)] } 

Found: { [Jerry (Person)] [654 (Number)] [James (Person)] }