| |
Main Menu | Next Section |
A. Designing an Iterator
Now imagine that the programmer working on the main program says
that she is uncomfortable with the fact that she is in charge
of some of the output and you as programmer for 'ListOfContracts'
are in charge of the rest. Remember that in chapter 8 we discussed
this issue
and decided to compromise and have
the main program handle the displaying of individual contracts
but have 'ListOfContracts' handle the 'DisplayAll' function. Code-wise this works well in the sense that 'DisplayAll' was easy to
write inside 'ListOfContracts'. However, every time the programmer
for 'Contracts' adds a new property, all three programmers have
to make changes to their code. That is not a good sign.
Some would argue that 'Contract' itself should handle any contract
output. Then, only one programmer (the one in charge of 'Contracts'
) would be required to make changes in the output functions when
a contract property was added (or deleted). However, imagine that
a number of different programs were going to use the Contract
class - one to prepare reports, one for billing, one for taxes,
one for advertising etc. Each of these would probably require
its own output format, and there is no way that the programmer
for the Contract class could conceive of all the ways the data
might be used.
For that reason many argue that a class like 'Contract' should
only provide a way to access the individual data members (the
'Provide' functions in our version) and leave how that data is
used (output, calculated with, etc.) to the code for specific
programs. That is the approach we will take here. Let's rewrite
the main program and 'ListOfContracts' so that the main program
handles 'DisplayAll'.
Remember, what makes implementing 'DisplayAll' difficult is that the main program has
no direct access to the individual contracts. It cannot on its
own go inside the list of contracts to access them one by one
for display purposes. One way to handle this is to include in
'ListOfContracts' the capability to iterate through the
list. Iteration in this context is the process of asking for the
'next' contract, displaying it, and stopping when there are not
more contracts.
What does 'ListOfContracts' need to handle this? First, there
must be some indicator of what the 'next' contracts is. This will
be an index into the array. It should start at 0 (the first contract
in the array) and increase each time a contract is accessed. There
also must be a function that returns the next contract. In addition,
there must be a function that allows a user program to ask if
there are still more contracts to get. Finally, since the user
of the program may want to display all the contracts more than
once, there needs to be a function that resets the indicator of
the next contract back to 0.
Below is the code for the part of the 'ListOfContracts' declaration
that shows these additions:
// The end of the Public part of the class .... void Reset(); Contract GetNext(); bool AreThereMore(); private: int numContracts; // indicates next available slot in list int presentMax; // present size of array int next; // Used to iterate through the list // Holds the 'next' unaccessed contract in a set of requests .... // The rest of the private part };What is not shown here is that the member function 'DisplayAll' has been deleted. In its place are
the three new member functions, which are all public since they
will be used in the main program. The data member 'next' is another
one of those variables that is only used inside the class. Therefore,
there are no public functions such as 'ProvideNext' or 'ChangeNext'
which would allow user programs to directly change the value of
'next'. Of course, 'Reset' and 'GetNext' will change the value
of this property but user programs do not have any reason to know
how these changes are being made.
B. Using the Iterator
To be different, let's skip for the moment the discussion of how
these new functions will be defined and move to a discussion of
the main program. We do this to emphasize that once everyone has
agreed on the interface (in this case, once everyone agrees on
the names, parameters and return types of the three new public
functions), work can proceed on both 'ListOfContracts' and the
main program in parallel.
The changes to the main program are quite simple and they all
occur in the function 'ProcessUserChoices'. To handle the "Display
All" choice, the previous version of this function, had
the lines
This, of course, calls the now non-existent member function that used to display all the contracts. In its place we include the code:
case 'L':
case 'l':
contracts.Reset();
for (; contracts.AreThereMore() ; )
{ contract = contracts.GetNext();
DisplayInfo(contract);
}
break;
The first line for this 'case' resets the list of contracts so
that the iterator will start with the first contract in the list.
That is simple enough but look at the 'for' statement! It
looks as if the programmer forgot a few things. Actually, this
is another example of versatility of this statement.
We know that there are three parts inside the parentheses of a
'for' statement. The first part handles any initializations, the
second part handles end of loop testing, and the third part handles
any required incrementing or other changing of values of the variables
used in the loop. As the code is written, there is no need for
any initialization because that was done by
To indicate that there are no initializations, we simply include
a semi-colon right inside the parenthesis.
After that parenthesis there is the call:
We know that 'AreThereMore' will return true or false so it works
well as the test condition. The loop will continue as long as
there are more contracts to get. Note that there is no semicolon
at the end of the line of code we just read although there is
one just after this same code in the for loop itself. The semicolon
in the 'for' loop is NOT part of the call. It is the sign to the
compiler that there are no more test conditions. In chapter 8
we discussed how the 'for' loop can be coded to have
more than one test condition. In the code we are discussing now
there is only one test and there is nothing after the second semi-colon.
Normally, this is where we would put any increments. The fact
that there is nothing here indicates that there are no increments
- at least none we need put here.
To see why this is so, we need to examine what goes on inside the loop. Part of the discussion about the interface of 'listOfContracts' must include a discussion of what will be expected of each public function - not 'how' each function will work but 'what' each function will do. In the case of 'GetNext', it needs to be decided if it will automatically handle somehow adjusting the instance to indicate the next contract, or if a special function needs to be called to do this. Some designs call for such a special function, with a name such as 'Continue' or 'Shift'. In our case, the fact that there is no addition member function in 'ListOfContracts' means that 'GetNext' must do the work. It will not only return the 'next' Contract, it will also adjust the data member 'next' to "point to" the contract after the one in the array just returned. Since this function does the work of adjusting 'next', there is no need for any incrementing or adjusting inside the 'for' statement parentheses.
Any part of a 'for' statement can be excluded. Here is an interesting version:
This is an infinite loop! It has no test conditions and therefore
has no way to stop! If you have the time (and a good book to read
while you wait) you might want to make a guess as to what this
outputs and then run it to see if you are right).
One can even right:
if you want an infinite loop to perform some activity unrelated
to loop conditions. This may seem very strange but some operating
systems might have a variation of this. Any program that can only
be stopped by turning off the system or somehow shutting it down
from outside itself, might have a loop like this.
Back to our discussion on the implementation of the for loop that
displays all the contracts: The loop will continue until all the
contracts have been displayed. Inside the loop, the system will
retrieve the next contract from the list and pass it on to the
already existing function 'DisplayInfo. Thus, we take advantage
of code that was already written. Note also, that this iterator
could be used for other purposes. For example, a program that
wanted to create bills for all the contracts could also retrieve
the contracts one by one, but instead of sending each one to a
display function, it could send them to a bill writing function.
This completes the changes to the main program. Note that this
time, because we are changing the interface between the class
and its users, we need to make changes on both sides.
C. Defining the Iterator Functions
It is now time to examine the code for the three new member functions.:
void ListOfContracts :: Reset()
{ next = 0;
}
Contract ListOfContracts :: GetNext()
{
return listOfContracts[next++];
}
bool ListOfContracts :: AreThereMore()
{ if (next < numContracts)
{ return true;
}
else
{ return false;
}
// More complex: the single line: return (next < numContracts)
}
'Reset' simply gives 'next' the value 0, the index of the first
array element. 'GetNext' returns the contract in the array element
indexed by 'next'.
The use of "++" here is a bit special.
Up to now we have emphasized that "++" increments by
1 the value stored in a variable. This is true but one can actually
put the "++" before or after the variable and the meanings
of these are different. The code cout << var1++
would cause a system to output the value in 'var1' and then increment
that value by 1. In other words, if 'var1' had the value 3 before
this line was executed, a 3 would be output and the value would
change to 4.
On the other hand, if the code was:
the value would be incremented first and then output. So, if 'var1' started with the value 3, that value would be incremented to 4 and 4 would then be output. To summarize: a "++" before a variable means to increment the value in the variable and then, if something is to be done, do it; a "++' after a variable means to do anything that is to be done with the value in the variable and then increment it. When the '++' is before the variable, we are using the prefix operator, and when the '++' is afrer the variable, we are using the postfix operator.
Note that when we have the variable with the "++" by itself as in
it really does not make a difference whether we write:
In neither case is the incremented value being used right away.
The same discussion is relevant to the "--" operator.
The code
would cause the value in 'index' to be output before the value
is decremented. But,
will cause the value to be decremented before it is output.
Returning to the case of:
the system will retrieve the contract in the array element indicated
by the value in 'next' before it is incremented; 'next' will then
be incremented; and finally, the retrieved contract is returned.
The 'return' makes it a bit difficult to see what is happening
so consider the following code which will have exactly the same
effect:
Here, the variable 'c1' will hold the contract in the array element
indicated by 'next' before it is incremented. The variable 'next
is then incremented and, finally, the return of the contract 'c1'
occurs. In this way, next is always pointing to the contract to
be retrieved next after 'GetNext' finishes'. Note that this function
could get into trouble if the user of this program does not check
if there is still another contract to retrieve (using the function
'AreThereMore'), before it calls 'GetNext' each time. Sooner or
later 'next' will "point" past the end of the array
and strange data will be returned. In programming terms, the results
are unpredictable.
The final function, 'AreThereMore', simply asks if 'next' has gone
through all the contracts:
and returns true or false depending on the answer. Note the comment
at the end of the function indicating a more complex way of writing
the code:
This says to directly return the result of the comparison. The
code "next < numContracts" will return 0 if it is
not true and some value other than 0 if it is true. The word 'return'
will then cause this value to be returned to the calling function.
Feel free to use this variation of the code if you feel comfortable
with it but both approaches are the same. It is presented here
so that you will have some idea of what is going on if you encounter
this form as you read other code - which you are, of course, doing
at every opportunity you have - if you really want to learn to
code!
That completes the discussion of iterators. To see this code run,
you can use the same version of class 'Contract' (contrct4.h and contrct4.cpp. Here are the complete versions of the modified code for
'ListOfContracts' (ch10lst5.h and ch10lst5.cpp) and the main program
(ch10tst7.cpp)
Topics Covered in the "Essentials of C++"
| |
Main Menu | Next Section |