Notes for week 4

For this week's links click here.

Creating Program for Example 5

Review of the Problem

We will be interested in modeling the interaction between objects in different classes. In the programs we have looked at so far, the run function has executed a series of function calls to objects from one class. How can we model interaction between objects of more than one class? We introduced scenarios, to help programmers visualize class interaction.

The Grocery List

The Class GroceryItem

In our first attempt to design a GroceryItem, we listed the following functions

Constructors

The constructors for GroceryItem are again relatively simple to build. First we need the trivial constructor. We will have an array, and when we initialize the array, its constructor calls on the trivial constructor for the data stored in the array. Second we need a constructor that will initialize the four data members (name, id, cost per item and order size).

Compute Cost, Write and Print

The computeCost, write and print functions are relatively straight forward and you can see them when you look at the complete solution.

Read from a file

The read from a file has one new twist. The extract operation on istreams is defined for variables of type double, but Borland's extract for doubles does not work well, so it is safer to read the double as a string and convert it. Here is the read function.

void GroceryItem::read(istream &in)
{
	getline(in,_name,'\n');
  	 if(_name =="") //if hanging end of line marker get again
   		 getline(in,_name,'\n');

  	 getline(in,_idNumber,'\n');
  	  if (_idNumber =="")
    		getline(in,_idNumber,'\n');
	string costString; // local string to hold cost
   	getline(in,costString,'\n');
  	 if(costString =="")
   		 getline(in,costString,'\n');
	_unitCost= atof(costString.c_str()); // convert the string to a double
	in >> _sizeOfOrder; // read the integer _sizeOfOrder
}

One extra function - the equality operator ==

Because GroceryItems will be stored in an array, and array has operators that use the == operator, you must define the == operator. To make c++ classes as much a part of the language as possible, you can override most of its operators. To override an operator for a given class you must first declare the operator in the class. Since the == operator returns an int, and compares a member of the class with the current item the declaration looks like this:

   int operator  ==(const GroceryItem &) const;
You will notice first that the reserved word operator is used to indicate that the name of this function is not a standard identifier. Next you will notice that the parameter is a GroceryItem, but it is a constant reference to a GroceryItem. This is a language requirement that promises that the GroceryItem that is passed to the operator will not be changed by it. The second thing you will notice is the const after the parameter list. This guarantees that the object calling the operator will not be changed by the operator. The main == operator for any class X is has a signature
     int operator == (const X &) const

The Class GroceryList

When we were designing the GroceryList we decided that the class would contain an array of GroceryItem. To see a description of the class Array click here. Containment is easy to implement in c++. We need only declare a member data item of this type. Click here to see the class description for GroceryList:

The GroceryList functions

One of the advantages of this implementation as opposed to making GroceyList a descendent of Array is that programmers using GroceryList don't accidentally use the bracket operator to add items to the list. This could cause the rest of the functions to work improperly. One thing that seems to be a disadvantage is that functions like the clearArray and insertItem are not available. While this means the programmer must write a few more functions, this level of insulation makes the program more flexible. If the method of insertion were changed, you do not have to add a function after the fact. Simply modify the exiting function to perform the new task. You can see that addItem is a trivial function here

void GroceryList::addItem(const GroceryItem &newItem)
{
	_item.insertItem(newItem);
}

A less trivial function is that uses the array functions is read. The first item in the file is the size of the file. Once this has been read, we read one GroceryItem at a time and use the GroceryList function addItem to add the item. Notice we might have replaced addItem with _items.insertItem(oneItem), but it is always better to use a function from the current class. If other insertItem was used and the addItem function were altered, we would have to make sure that all functions that added items were modified appropriately. This way we know our additions will be consistent. Click here to see the read function.

When we get to the looping problems we find a new feature of object-oriented programming-the iterator. Most structures in c++ have at least one associated class called an iterator. Iterators have methods for traversing the structure. This may seem like overkill, but structures like trees may have many different traversals associated with the same tree. In addition, traversals usually require special knowledge of the current position in the structure. This knowledge is often stored in a set of pointers, but may be stored in yet another structure such as a stack or queue. These storage locations add extra complications to the structure that may only be needed on occasion, so moving them to the iterator makes your class simpler.

A simple example of a function that requires an iterator is the computeCost function. This is a relatively simple function conceptually. Simply loop through the array, and add the cost computed by each item to a total. How do we loop through the array?

First we create an ArrayIterator for GroceryItem. Notice that we pass in the array _item to the constructor. This is actually pass by reference, so any changes made by the iterator will be saved in _item. The iterator is automatically initialized to point to the first item in the structure. The iterator variable next can be used as an integer in the conditional part of the while loop, since this expects an integer expression. The class iterator has an operator that converts next to an integer in such a way that the value is 1 if you are not at the end of the array and 0 if you are at the end. The function operator () is used to retrieve a reference to the current item in the array. This means that next() is a GroceryItem, and we can call on it to compute its cost. Finally to move ahead in the array we use the ++ operator, which advances the current position to the next position the list. Click here for iterator code.

The Application Class

Next week