Free Trial

Safari Books Online is a digital library providing on-demand subscription access to thousands of learning resources.


  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • DownloadDownload
  • PrintPrint
Share this Page URL
Help

Week: 3 At a Glance > In Review

Week 3. In Review

The following program (as shown in Listing R3.1) brings together many of the advanced techniques you've learned during the past three weeks of hard work. Week 3 in Review provides a template-based linked list with exception handling. Examine it in detail; if you understand it fully, you are a C++ programmer.

Caution

If your compiler does not support templates, or if your compiler does not support try and catch, you will not be able to compile or run this listing.


Listing R3.1. Week 3 in Review Listing

  0:  // **************************************************
  1:  //
  2:  // Title:     Week 3 in Review
  3:  //
  4:  // File:      Week3
  5:  //
  6:  // Description:   Provide a template-based linked list
  7:  //                demonstration program with exception handling
  8:  //
  9:  // Classes:    PART - holds part numbers and potentially other
 10:  //                      information about parts. This will be the
 11:  //                      example class for the list to hold.
 12:  //                      Note use of operator<< to print the
 13:  //                      information about a part based on its
 14:  //                      runtime type.
 15:  //
 16:  //               Node - acts as a node in a List
 17:  //
 18:  //               List - template-based list that provides the
 19:  //                      mechanisms for a linked list
 20:  //
 21:  //
 22:  // Author:    Jesse Liberty (jl)
 23:  //
 24:  // Developed:   Pentium 200 Pro. 128MB RAM MVC 5.0
 25:  //
 26:  // Target:    Platform independent
 27:  //
 28:  // Rev History:  9/94 - First release (jl)
 29:  //               4/97 - Updated (jl)
 30:  //               9/04 – Updated (blj)
 31:  // **************************************************

 32:  #include <iostream>

 33:  using namespace std;
 34:
 35:  // exception classes

 36:  class Exception {};
 37:  class OutOfMemory :   public Exception{};
 38:  class NullNode :    public Exception{};
 39:  class EmptyList :   public Exception {};
 40:  class BoundsError :   public Exception {};
 41:
 42:
 43:  // **************** Part ************
 44:  // Abstract base class of parts
 45:  class Part
 46:  {
 47:    public:
 48:      Part():itsObjectNumber(1) {}
 49:      Part(int ObjectNumber):itsObjectNumber(ObjectNumber){}
 50:      virtual ~Part(){};
 51:      int GetObjectNumber() const { return itsObjectNumber; }
 52:      virtual void Display() const =0;  // must be overridden
 53:
 54:    private:
 55:      int itsObjectNumber;
 56:  };
 57:
 58:  // implementation of pure virtual function so that
 59:  // derived classes can chain up
 60:  void Part::Display() const
 61:  {
 62:     cout << "\nPart Number: " << itsObjectNumber << endl;
 63:  }
 64:
 65:  // this one operator<< will be called for all part objects.
 66:  // It need not be a friend as it does not access private data
 67:  // It calls Display(), which uses the required polymorphism
 68:  // We'd like to be able to override this based on the real type
 69:  // of thePart, but C++ does not support contravariance

 70:  ostream& operator<<( ostream& theStream,Part& thePart)
 71:  {
 72:     thePart.Display();  // virtual contravariance!

 73:     return theStream;
 74:  }
 75:
 76:  // **************** Car Part ************
 77:  class CarPart : public Part
 78:  {
 79:    public:
 80:      CarPart():itsModelYear(94){}
 81:      CarPart(int year, int partNumber);
 82:      int GetModelYear() const { return itsModelYear; }
 83:      virtual void Display() const;
 84:    private:
 85:      int itsModelYear;
 86:  };
 87:
 88:  CarPart::CarPart(int year, int partNumber):
 89:     itsModelYear(year),
 90:     Part(partNumber)
 91:  {}
 92:
 93:  void CarPart::Display() const
 94:  {
 95:    Part::Display();
 96:    cout << "Model Year: " << itsModelYear << endl;
 97:  }
 98:
 99:  // **************** AirPlane Part ************
100:  class AirPlanePart : public Part
101:  {
102:    public:
103:      AirPlanePart():itsEngineNumber(1){};
104:      AirPlanePart(int EngineNumber, int PartNumber);
105:      virtual void Display() const;
106:      int GetEngineNumber()const { return itsEngineNumber; }
107:    private:
108:      int itsEngineNumber;
109:  };
110:
111:  AirPlanePart::AirPlanePart(int EngineNumber, int PartNumber):
112:     itsEngineNumber(EngineNumber),
113:     Part(PartNumber)
114:  {}
115:
116:  void AirPlanePart::Display() const
117:  {
118:     Part::Display();
119:     cout << "Engine No.: " << itsEngineNumber << endl;
120:  }
121:
122:  // forward declaration of class List
123:  template <class T>
124:  class List;
125:
126:  // ****************  Node ************
127:  // Generic node, can be added to a list
128:  // ************************************
129:

130:  template <class T>
131:  class Node
132:  {
133:    public:

134:      friend class List<T>;
135:      Node (T*);
136:      ~Node();
137:      void SetNext(Node * node) { itsNext = node; }
138:      Node * GetNext() const;

139:      T * GetObject() const;
140:    private:
141:      T* itsObject;
142:      Node * itsNext;
143:  };
144:
145:  // Node Implementations...
146:

147:  template <class T>
148:  Node<T>::Node(T* pOjbect):
149:     itsObject(pOjbect),
150:     itsNext(0)
151:  {}
152:
153:  template <class T>
154:  Node<T>::~Node()
155:  {
156:     delete itsObject;
157:     itsObject = 0;
158:     delete itsNext;
159:     itsNext = 0;
160:  }
161:
162:  // Returns NULL if no next Node
163:  template <class T>
164:  Node<T> * Node<T>::GetNext() const
165:  {
166:     return itsNext;
167:  }
168:

169:  template <class T>
170:  T * Node<T>::GetObject() const
171:  {
172:     if (itsObject)
173:        return itsObject;
174:     else
175:        throw NullNode();
176:  }
177:
178:  // ****************  List ************
179:  // Generic list template
180:  // Works with any numbered object
181:  // ***********************************
182:  template <class T>
183:  class List
184:  {
185:    public:
186:      List();
187:      ~List();
188:

189:      T*        Find(int & position, int ObjectNumber)  const;
190:      T*      GetFirst() const;
191:      void      Insert(T *);
192:      T*      operator[](int) const;
193:      int      GetCount() const { return itsCount; }
194:    private:

195:      Node<T> * pHead;
196:      int      itsCount;
197:  };
198:
199:  // Implementations for Lists...
200:  template <class T>
201:  List<T>::List():
202:     pHead(0),
203:     itsCount(0)
204:  {}
205:

206:  template <class T>
207:  List<T>::~List()
208:  {
209:     delete pHead;
210:  }
211:
212:  template <class T>
213:  T*   List<T>::GetFirst() const
214:  {
215:     if (pHead)
216:        return pHead->itsObject;
217:     else
218:        throw EmptyList();
219:  }
220:

221:  template <class T>
222:  T *  List<T>::operator[](int offSet) const
223:  {
224:     Node<T>* pNode = pHead;
225:
226:     if (!pHead)
227:        throw EmptyList();
228:
229:     if (offSet > itsCount)
230:        throw BoundsError();
231:
232:     for (int i=0;i<offSet; i++)
233:        pNode = pNode->itsNext;
234:
235:     return   pNode->itsObject;
236:  }
237:
238:  // find a given object in list based on its unique number (id)

239:  template <class T>
240:  T*   List<T>::Find(int & position, int ObjectNumber)  const
241:  {
242:     Node<T> * pNode = 0;
243:     for (pNode = pHead, position = 0;
244:          pNode!=NULL;
245:          pNode = pNode->itsNext, position++)
246:     {
247:        if (pNode->itsObject->GetObjectNumber() == ObjectNumber)
248:          break;
249:     }
250:     if (pNode == NULL)
251:        return NULL;
252:     else
253:        return pNode->itsObject;
254:  }
255:
256:  // insert if the number of the object is unique

257:  template <class T>
258:  void List<T>::Insert(T* pObject)
259:  {
260:     Node<T> * pNode = new Node<T>(pObject);
261:     Node<T> * pCurrent = pHead;
262:     Node<T> * pNext = 0;
263:
264:     int New =  pObject->GetObjectNumber();
265:     int Next = 0;
266:     itsCount++;
267:
268:     if (!pHead)
269:     {
270:        pHead = pNode;
271:        return;
272:     }
273:
274:     // if this one is smaller than head
275:     // this one is the new head
276:     if (pHead->itsObject->GetObjectNumber() > New)
277:     {
278:        pNode->itsNext = pHead;
279:        pHead = pNode;
280:        return;
281:     }
282:
283:     for (;;)
284:     {
285:        // if there is no next, append this new one
286:        if (!pCurrent->itsNext)
287:        {
288:          pCurrent->itsNext = pNode;
289:          return;
290:        }
291:
292:        // if this goes after this one and before the next
293:        // then insert it here, otherwise get the next
294:        pNext = pCurrent->itsNext;
295:        Next = pNext->itsObject->GetObjectNumber();
296:        if (Next > New)
297:        {
298:          pCurrent->itsNext = pNode;
299:          pNode->itsNext = pNext;
300:          return;
301:        }
302:        pCurrent = pNext;
303:     }
304:  }
305:
306:
307:  int main()
308:  {

309:     List<Part> theList;
310:     int choice = 99;
311:     int ObjectNumber;
312:     int value;
313:     Part * pPart;
314:     while (choice != 0)
315:     {
316:        cout << "(0)Quit (1)Car (2)Plane: ";
317:        cin >> choice;
318:
319:        if (choice != 0)
320:        {
321:
322:           cout << "New PartNumber?: ";
323:           cin >>  ObjectNumber;
324:
325:           if (choice == 1)
326:           {
327:             cout << "Model Year?: ";
328:             cin >> value;

329:             try
330:             {
331:               pPart = new CarPart(value,ObjectNumber);
332:             }

333:             catch (OutOfMemory)
334:             {
335:               cout << "Not enough memory; Exiting..." << endl;
336:               return 1;
337:             }
338:           }
339:           else
340:           {
341:             cout << "Engine Number?: ";
342:             cin >> value;

343:             try
344:             {
345:               pPart = new AirPlanePart(value,ObjectNumber);
346:             }
347:             catch (OutOfMemory)
348:             {
349:               cout << "Not enough memory; Exiting..." << endl;
350:               return 1;
351:             }
352:           }

353:           try
354:           {
355:             theList.Insert(pPart);
356:           }

357:           catch (NullNode)
358:           {
359:             cout << "The list is broken, and the node is null!" << endl;
360:             return 1;
361:           }

362:           catch (EmptyList)
363:           {
364:             cout << "The list is empty!" << endl;
365:             return 1;
366:           }
367:        }
368:     }

369:     try
370:     {
371:        for (int i = 0; i < theList.GetCount(); i++ )
372:          cout << *(theList[i]);
373:     }

374:        catch (NullNode)
375:        {
376:          cout << "The list is broken, and the node is null!" << endl;
377:          return 1;
378:        }
379:        catch (EmptyList)
380:        {
381:          cout << "The list is empty!" << endl;
382:          return 1;
383:        }

384:        catch (BoundsError)
385:        {
386:          cout << "Tried to read beyond the end of the list!" << endl;
387:          return 1;
388:        }
389:     return 0;
390:  }


					  


(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 2837
Model Year? 90

 (0)Quit (1)Car (2)Plane: 2
New PartNumber?: 378
Engine Number?: 4938

 (0)Quit (1)Car (2)Plane: 1
New PartNumber?: 4499
Model Year? 94

 (0)Quit (1)Car (2)Plane: 1
New PartNumber?: 3000
Model Year? 93

 (0)Quit (1)Car (2)Plane: 0

Part Number: 378
Engine No. 4938

Part Number: 2837
Model Year: 90

Part Number: 3000
Model Year: 93

Part Number 4499
Model Year: 94

The Week 3 in Review listing modifies the program provided in Week 2 to add templates, ostream processing, and exception handling. The output is identical.


On lines 36–40, a number of exception classes are declared. In the somewhat primitive exception handling provided by this program, no data or methods are required of these exceptions; they serve as flags to the catch statements, which print out a very simple warning and then exit. A more robust program might pass these exceptions by reference and then extract context or other data from the exception objects in an attempt to recover from the problem.

On line 45, the abstract base class Part is declared exactly as it was in Week 2. The only interesting change here is in the nonclass member operator<<(), which is declared on lines 70–74. Note that this is neither a member of Part nor a friend of Part, it simply takes a Part reference as one of its arguments.

You might want to have operator<< take a CarPart and an AirPlanePart in the hopes that the correct operator<< would be called, based on whether a car part or an airplane part is passed. Because the program passes a pointer to a part, however, and not a pointer to a car part or an airplane part, C++ would have to call the right function based on the real type of one of the arguments to the function. This is called contravariance and is not supported in C++.

You can only achieve polymorphism in C++ in two ways: function polymorphism and virtual functions. Function polymorphism won't work here because in every case you are matching the same signature: the one taking a reference to a Part.

Virtual functions won't work here because operator<< is not a member function of Part. You can't make operator<< a member function of Part because you want to invoke

cout << thePart

and that means that the actual call would be to cout.operator<<(Part&), and cout does not have a version of operator<< that takes a Part reference!

To get around this limitation, the Week 3 program uses just one operator<<, taking a reference to a Part. This then calls Display(), which is a virtual member function, and thus the right version is called.

On lines 130–143, Node is defined as a template. It serves the same function as Node did in the Week 2 Review program, but this version of Node is not tied to a Part object. It can, in fact, be the node for any type of object.

Note that if you try to get the object from Node, and there is no object, this is considered an exception, and the exception is thrown on line 175.

On lines 182 and 183, a generic List class template is defined. This List class can hold nodes of any objects that have unique identification numbers, and it keeps them sorted in ascending order. Each of the list functions checks for exceptional circumstances and throws the appropriate exceptions as required.

On lines 307 and 308, the driver program creates a list of two types of Part objects and then prints out the values of the objects in the list by using the standard streams mechanism.

FAQ

In the comment above line 70, you mention that C++ does not support contravariance. What is contravariance?

Answer: Contravariance is the ability to assign a pointer to a base class to a pointer to a derived class.


If C++ did support contravariance, you could override the function based on the real type of the object at runtime. Listing R3.2 won't compile in C++, but if it supported contra-variance, it would…

Caution

This listing will not compile!


Listing R3.2. Contravariance

 0:  #include <iostream>
 1:  using namespace std;
 2:  class Animal
 3:  {
 4:    public:
 5:      virtual void Speak()
 6:         { cout << "Animal  Speaks" << endl; }
 7:  };
 8:
 9:  class Dog : public Animal
10:  {
11:    public:
12:      void Speak() { cout << "Dog Speaks" << endl; }
13:  };
14:
15:
16:  class Cat : public Animal
17:  {
18:    public:
19:      void Speak() { cout << "Cat Speaks" << endl; }
20:  };
21:
22:  void DoIt(Cat*);
23:  void DoIt(Dog*);
24:
25:  int main()
26:  {
27:     Animal * pA = new Dog;
28:     DoIt(pA);
29:     return 0;
30:  }
31:
32:  void DoIt(Cat * c)
33:  {
34:     cout << "They passed a cat!" << endl << endl;
35:     c->Speak();
36:  }
37:
38:  void DoIt(Dog * d)
39:  {
40:     cout << "They passed a dog!" << endl << endl;
41:     d->Speak();
42:  }


					  

What you can do, of course, is to use a virtual function as shown in Listing R3.3, which partially solves the problem.

Listing R3.3. Using Virtual Functions

 0:  #include<iostream>
 1:  using namespace std;
 2:
 3:  class Animal
 4:  {
 5:    public:
 6:      virtual void Speak() { cout << "Animal Speaks" << endl; }
 7:  };
 8:
 9:  class Dog : public Animal
10:  {
11:    public:
12:      void Speak() { cout << "Dog Speaks" << endl; }
13:  };
14:
15:
16:  class Cat : public Animal
17:  {
18:    public:
19:      void Speak() { cout << "Cat Speaks" << endl; }
20:  };
21:
22:  void DoIt(Animal*);
23:
24:  int main()
25:  {
26:
27:     Animal * pA = new Dog;
28:     DoIt(pA);
29:     return 0;
30:  }
31:
32:  void DoIt(Animal * c)
33:  {
34:     cout << "They passed some kind of  animal" << endl << endl;
35:     c->Speak();
36:  }


					  

 

  • Safari Books Online
  • Create BookmarkCreate Bookmark
  • Create Note or TagCreate Note or Tag
  • DownloadDownload
  • PrintPrint