Previous Section  < Free Open Study >  Next Section

Selected C++ Example #18


// Example #18 



// This example code shows the attempt to implement three

// different lists (meal lists, airplane lists, and dog lists)

// with one implementation of the list code. The general approach

// is to use C++ templates to capture the common structure

// that all LinkedLists share. The template is then used to

// generate the three different lists. The advantage of templates

// over weakened type checking via inheritance (Example #17) is

// that mistakes of flying dogs off runways cannot occur (due to

// the maintenance of strong type checking by templates). The

// disadvantage is that we pay for the class/method code for each

// new data type that wishes to have a list instantiation.



#include <iostream.h>

#include <new.h>



// The derived classes Dog, Meal, and Airplane are only

// skeleton classes, to simplify their implementation.

class Dog {

public:

     void bark();

     void bite();

     void print(ostream& o = cout);

};



void

Dog::bark()

{

     cout << ''Bow Wow\n'';

}



void

Dog::bite()

{

     cout << ''Ouch!!!\n'';

}



void

Dog::print(ostream& o)

{

     o << ''I am a dog!\n'';

}



classMeal {

public:

     void eat();

     void print(ostream& o = cout);

};



void

Meal::eat()

{

  cout << ''Crunch ... Munch ... Crunch ...\n'';

}



void

Meal::print(ostream& o)

{

     o << ''I'm a meal\n'';

}



class Airplane {

public:

     void fly();

     void print(ostream&o = cout);

};



void

Airplane::fly()

{

     cout << ''Va-a-a--room!!!\n'';

}





void

Airplane::print(ostream& o)

{

     o << ''I'm an airplane!\n'';

}



// Borland wouldn' t accept nested templates, so the Node class is

// placed outside the scope of the LinkedList.

template <class DATUM>

struct Node {

     DATUM data;

     Node* next;

public:

     Node(DATUM&);

};



template <class DATUM>

class LinkedList{

      Node<DATUM>* head;

      int len;

public:

     LinkedList();

     ~LinkedList();

     int insert(DATUM&);

     DATUM remove();

     void traverse() const;

     int length();

};





template <class DATUM>

Node<DATUM>::Node(DATUM& new_item)

{

     data = new_item;

     next = NULL;

}





template <class DATUM>

LinkedList<DATUM>::LinkedList()

{

     head = NULL;

     len = 0;

}



template <class DATUM>

LinkedList<DATUM>::~LinkedList()

{

     Node<DATUM>* temp;



     while (head != NULL) {

         temp = head;

         head = head->next;

         delete temp;

     }

}





template <class DATUM>

int

LinkedList<DATUM>::insert(DATUM& new_item)

{

     Node<DATUM>* temp = head;



     if (temp == NULL) {

          head = new Node<DATUM>(new_item);

    }

     else {

          while (temp->next != NULL) {

                     temp = temp->next;

          }

          temp->next = new Node<DATUM>(new_item);

     }

     return(++len);

}





template <class DATUM>

DATUM

LinkedList<DATUM>::remove()

{

     Node<DATUM>* temp = head;

     static DATUM bad_item;

     DATUM retval;



     if (temp == NULL) {

          return(bad_item);

     }

     else {

          retval = head->data;

          head = head->next;

          len--;

          delete temp;

          return(retval);

     }

}





// Notice that the traverse method sends a message to the

// data in each node to print itself. This sets up a requirement

// that any data type that wants to be in a LinkedList needs

// a print method. An even more important consideration is

// the fact that the syntax is different if DATUM is a pointer

// or nonpointer. Nonpointers would use a dot, and not

// an arrow, operator. The template requires its DATUM to be

// a pointer (in this example), and that the DATUM possess a

// print method.

template <class DATUM>

void

LinkedList<DATUM>::traverse() const

{

     Node<DATUM>* temp = head;



     cout << ''(\n'';

     while (temp != NULL) {

         temp->data->print();

         cout << ''\n'';

         temp = temp->next;

     }

     cout << '')\n\n'';

}





template <class DATUM>

int

LinkedList<DATUM>::length()

{

     return(len);

}





void

main()

{

     LinkedList<Meal*> MealList;

     LinkedList<Airplane*> AirplaneList;

     LinkedList<Dog*> DogList;



// The following template expansion would generate errors due to

// the traverse method requiring a pointer data type (or at least

// a data type that supports the ''->'' operator).

//

//     LinkedList<Dog> x;

//

      

      Meal *meal1 = new Meal, *meal2 = new Meal, *meal3 = new Meal;

      Dog *dog1 = new Dog, *dog2 = new Dog, *dog3 = new Dog;

      Airplane *air1 = new Airplane, *air2 = new Airplane, *air3 =

                       new Airplane;

                       

// At first glance everything seems to work nicely 

      MealList.insert(meall);

      MealList.insert(meal2);

      MealList.insert(meal3);

      Meal* aMeal = (Meal*) MealList. remove();

      aMeal->eat();

      char c;

      cin >> c;



      AirplaneList.insert(air1);

      AirplaneList.insert(air2);

      AirplaneList.insert(air3);



      DogList.insert(dog1);

      DogList.insert(dog2);

      DogList.insert(dog3);



      MealList.traverse();

      AirplaneList.traverse();

      DogList.traverse();



      cin >> c;



// The following line of code would generate compiler errors due to

// the preservation of strong type checking by the C++ template

// mechanism.

//

//         AirplaneList.insert(dog2);



      delete meal1;

      delete meal2;

      delete meal3;

      delete dog1;

      delete dog2;

      delete dog3;

      delete air1;

      delete air2;

      delete air3;

}

    Previous Section  < Free Open Study >  Next Section