Previous Section Main Menu Next Section

CHAPTER 8

AN EVEN BETTER CONTRACT PROGRAM


Section VII: Improving ListOfContracts Section I: Analyzing the Code Implications of an Improved Contract Program Section II:Instances as Array Elements Section III: The List: A Look at Data Representation
Section IV: Lists: Working with Encapsulation Section V: Class Declarations for Contract and ListOfContracts Section VI: Member Functions for ListOfContracts Section VIII: Rewriting the Nonmember Functions

A. A Second Look at the 'for' Statement
That is all there is to the function definitions for 'ListOfContracts'. As is so often the case, however, such code can be written in more than one way. One possible change is in the search code and involves the 'for' statement which is actually much more powerful than we have let on so far. Using this statement, the search routines we saw above could have been written as follows:

When we discussed the 'for' statement in chapter 3, Section VIII. B, we saw that it had three distinct parts within the parentheses - with the first part used to initialize any variables, the second part used to test the stopping conditions, and the third part used to increment variables if necessary. In the code above, there are two possible stopping conditions so each is tested in the second part. One can read this as "Keep looping as long as 'index' is less than 'numContracts' AND 'found' is FALSE". Note how the parts of a 'for' statement are separated by semicolons, while separate elements within a part are separated by commas.

Note also that in this example, the variable 'index' is used in the loop but not declared in the loop. Up to now, we have usually written code such as:

for (int index = 0; index < numContracts && !found; index++)

with the word 'int' included in the parentheses. This is simply a matter of convenience - if one needs a variable for a 'for' loop and the variable has not been declared, do so when you initialize the variable. In the example here, however, we are making a quick change to existing code. In that code 'index' is already declared, so let's take advantage of that fact and not declare it again.

It turns out that the 'for' statement is one of the most versatile statements in C++. We shall see more of this versatility later.

A second change we might consider making involves the repeated use of the same search code in 'Add', 'Delete' and 'ReplaceContract'. You can see that each of these member functions has the same lines of code:

while ((!found) && (index < numContracts)) // keep searching until ID found
															  // or no more contracts
	{	if (contractNum == listOfContracts[index].ProvideID())
		{	found = true;
		}
		else
		{	index++;
		}
	}
Why not make this code a separate function called by all three functions using this code. This new function would only be used by member functions, so it could be declared as private to the class. (Code using instances of this class would still have access to 'FindContract' for doing searches.) Note that this idea of private functions is not something foreign to the way the 'real world' operates. Organizations often have procedures known only within the organization. That is part of what makes organizations different from each other.

The first step in this change is to declare our new function, call it 'Search', as private to the class. Here is a fragment of the modified .h file for 'ListOfContracts' showing this declaration:

   private:
      int numContracts;  // indicates next available slot in list
      ContractArray listOfContracts;

      int Search(int contractID);
      /*   Purpose:   To search through the list of contracts for a contract with an ID
                      that matches 'contractID'.
           
           Receives:  a contract's ID
           Returns:   the index of the contract with a matching ID or the value of
                       'numContracts' to indicate that there was no match
      */
  
Note that 'Search' is the only function declared in the private part of the class. Because all the other functions must be available to programs using this class, they are declared as public functions. 'Search' should be unavailable outside the class just as the data members are unavailable so it is declared as private. In other words, 'Search' and the data members are all encapsulated.

Regarding the declaration itself: all the searches use the contractID, so it must be received by the function. A careful look at the old code might lead you to expect that this function should return a Boolean, since all the searches set a variable 'found' to true or false. Unfortunately, there is a small complication here that won't let us do that. The member functions, 'Delete' and 'ReplaceContract', both need to know the location of the contract to be deleted or replaced. If our 'Search' function just returns true or false, these functions will not know where the contract was found. Therefore, we will return the location where the contract was found, that is, the value of 'index'. Note that if 'index' has been incremented so much that it has the value found in 'numContracts', we know that the list has been fully searched and the ID was not found. (Remember, 'numContracts' holds the location of the next free element of the array of contracts. Thus, if 'Search' returns the value in 'numContracts' (through the value of 'index'), it is an indication that all contracts were examined and none matched.)

A look at the definition for 'Search' might help. We will use a 'for' loop version, just to show some variation.


	int ListOfContracts ::  Search(int contractID)
	{
		int index;
		for (index = 0; index < numContracts; index++)
		{	if (contractID == listOfContracts[index].ProvideID())
				{	return index;
				}
		}
		return index;
	}

Since 'Search' is a member function of 'ListOfContracts' (even if it is a private member function) we must include ' ListOfContracts ::' in the first line of the definition. Inside, the 'for' statement only has one test condition because we again take advantage of the effect of the 'return' statement to end processing. As you can see, the value of 'index' is returned. If the sought after contract ID is found, the index of the matching element is returned. If the loop completes, it is because index has reached the value of 'numContracts' and this is returned to indicate the failure to find a match.

Now that we have this function, we can simplify each of the functions that used to do their own searching. Consider 'ReplaceContract':


	void ListOfContracts :: ReplaceContract (Contract contract)
	{
		int index;;
		int ID = contract.ProvideID();  // Get ID of the contract so we can find it in
											  // in the list.
		index = Search(ID);
		if (index < numContracts) // the contract to be replaced exists
		{	listOfContracts[index] = contract;
		}
		else
		{	cout << "\nContract to be replaced does not exist in the list.\n";
		}
	}
The ID of the contract to be inserted is retrieved; the search is made; and, if the value returned by "Search (index)" is less than 'numContracts', the replacement takes place. Note that it is much easier to understand this function with the details of the search moved out. As noted earlier, this is an important advantage in using functional decomposition.

The other two functions, as rewritten, are included below.


	void ListOfContracts :: Add(Contract contract)
	{
		if (numContracts < MAX_CONTRACTS)	   // is there room in the list?
		{

			int contractNum = contract.ProvideID();
			int index = Search(contractNum);

			
			if (index == numContracts) //contract with this ID does not exist
			{
				listOfContracts[numContracts] = contract; 
					// copy contract into list
				numContracts++;
			}
			else
			{	cout << "\nA contract with this ID already exists.\n";
			}
		}
		else
		{	cout << "There was no room to add the contract\n\n";
		}
	}

	void ListOfContracts :: Delete(int contractNum)
	{
		int index;
		index = Search(contractNum);
		if (index <  numContracts) // the contract to be deleted exists
		{	for (int i = index; i < numContracts - 1; i++)
			{	listOfContracts[i] = listOfContracts[i+1];
			}
			numContracts--;
		}
		else
		{	cout << "Invalid Contract ID\n\n";
		}
	}
The one thing to point out about this code is the way the 'Add' function checks to see if a contract already exists with the ID of the proposed new contract. This is accomplished in the line:

if (index == numContracts)

In other words, if 'index' equals 'numContracts', we know the search routine went through the whole list of contracts without finding a match.

You might have noticed that we did not modify 'FindContract' to use 'Search'. You are encouraged to try this as an exercise.

Topics Covered in the "Essentials of C++"

The 'for' Statement

Top of Section Main Menu Next Section