cs1ch10sec8.htm
 CHAPTER 10

ANOTHER VISIT WITH THE CONTRACT PROGRAM
OR
WORKING WITH DYNAMICALLY SIZED LISTS


Section VIII: Modifying the Main Program Section I: An Expandable List of Contracts Section II: Declaring the New ListofContracts Section III: Defining the New ListOfContracts
Section IV : Re-Evaluating the Contract Program Design Section V: Adding String Properties to the Contract Class Section VI: Declaring the New Contract Class Section VII: Defining the New Contract Class

  


Table of Contents

Learning C++:
An Index of Entry Points


2. The

of C++

A reference document on the basic elements of C++.



3. The Patterns



Index!



Notice that our modified version of the problem narrative made no mention of changes to the 'ListOfContracts' class. That is because, here, we are only adding strings and those additions have no effect on the list itself. Therefore, there are no changes to the .h or .cpp files for this class, except to change the include statement to include the new version of the 'Contract' class header file, contrct6.h. The files with those few changes are called ch10lst1.h and ch10lst1.cpp. (Note that the changes to 'ListOfContracts' required to implement the iterator were completed earlier.)

However, there are changes to the main program - mostly because of the changes to the interface we worked out in the analysis phase above. Here is where we use the string input operations we learned earlier. The changes in the input interface are more complex so let's start with them.

When a user selects the option to change contract information, the function 'ChangeInfo' is called. The original algorithm for this function repeatedly asked the user for a property code (a character) until a 'N' was entered to indicate that no more property values were to be changed. For each valid property code entered, the user was asked for the new value and the change was made.

Our analysis of this interface indicated that it would be faster if the user was allowed to enter all the codes for changes at once. Now that we know how to work with strings, we have a data structure that will allow us to do this. We will use a function called 'GetProperties' to retrieve the codes entered. However, instead of returning simply a character, this function will return a string.

We will explore how to implement this function in a bit but first let's continue our discussion of 'ChangeInfo'. Once the function has a string of codes, it needs to execute a loop to process the codes. The old loop continued until an 'N' was entered and we could continue to force the user to enter an 'N' in the new version. However, strings have their own natural ending symbol, the null character. Thus, we can write the loop to continue until the '\0' symbol is encountered in the string of codes.

Inside the loop in the old version there was a switch statement for each of the properties and separate calls to functions to get the new values. For example, there was the function 'GetSquareFootage' to ask the user for a new value for the square footage of an office and a function 'GetNumDesks' to get the new value for the 'numDesks' data member. As we add more properties, this is going to get ugly real fast. Unfortunately, we still need the switch statement itself to process the different codes but we can avoid having to create unique value requesting functions for each property. Instead we will create a general purpose input function for all the new string properties. This function will allow us to ask the user for the value of a specific property by passing in the name of that property. It will return the new string value for that property.

Here is the function definition for this function:

    void GetNewString(String80 string, String80 property)
    {
      cout << "Please enter the new " << property << endl;
      cin.ignore(80, '\n'); // clear out any unprocessed characters - especially \0
      cin.get(string, 81);
    }

It has two string parameters – with type names that must have been declared elsewhere. (We will use the type name approach here – as opposed to using 'char*' – simply to demonstrate that it can be done.)

The first parameter will be used to return the new value while the second will be used to receive and output the name of the property to be changed. Note how the 'cout' line uses this second parameter. For example, if the string 'Street Address' is passed in, the screen will display:

"Please enter the new Street Address"

If one wanted completely unique prompts, one could pass in the whole prompt and rewrite the cout line simply as:

cout << property << endl;

The third line of this function represents the first time we have used the 'get' input function you were introduced to in Chapter 9, Section IV. "Get(string, 81)", as you may recall, will read until it encounters an newline symbol or up to 80 characters - the 81st character is for the '\0'. Thus the user can type any length of string he or she wants up to 80 characters. The code is written 'cin.get(....)' because 'get' is actually a member function of the class istream of which 'cin' is an instance.

Unlike the '<<' operator, the 'get' member function does not skip white space or newline characters before reading in data. Nor, does it actually read the newline character when it encounters it. Thus, if the system has used the 'get' function and encountered a newline character, that newline character remains in the input stream. Therefore, the next 'get' will again encounter that newline character and stop immediately without reading anything! To avoid this we include the second line:

cin.ignore(80, '\n');

As the comment says, this skips past ("consumes") any left over characters until it has skipped over 80 characters or the newline character. In our program, there are unlikely to be any left over characters but there will be newlines left over from previous inputs. With the newline out of the way, the next 'get' function will operate safely.

As an aside, here you see another function that is so general it is really a pattern. You can use this approach of passing a string that represents part or all of an output anywhere you want to use one function to generate multiple different outputs.

We return now to 'ChangeInfo'. Since the ' GetNewString' function we just discussed returns a string, it cannot be used to retrieve new values for the integer based properties. We could write one function that would return an integer but the separate functions for these properties already exist so we will continue to use them. What we will change is the codes for these properties. We could easily make up codes that somehow matched the properties when we had only a few properties but as the number of properties have grown, too many of the property names have started with the same letter. Therefore the codes have been changed. It so happens that we only have eight properties so the codes 1-8 work just fine. If we had more properties, we would have to start using letter codes. We can't use a code such as '10' because the combined set of codes entered are stored as a string and the program would interpret this is the separate codes '1' and '0'.

Here is the C++ code based on this discussion:


void 	ChangeInfo( Contract& contract)
{       String80 string;
	String80 properties;
	GetProperties(properties);   // 'properties' is a string representing the codes for
                                     // all the properties to be changed.

	int index = 0;
	while (properties[index])
	{	switch (properties[index])
		{  case '1':
				GetNewString(string, "ContracteeName");
				contract.ChangeContracteeName(string);
				break;

			case '2':
				GetNewString(string, "Office Street Address");
				contract.ChangeOfficeStreetAddress(string);
				break;
			case '3':
				GetNewString(string, "Office City");
				contract.ChangeOfficeCity(string);
				break;
			case '4':
				GetNewString(string, "Office State");
				contract.ChangeOfficeState(string);
				break;
			case '5':
				GetNewString(string, "Office Zip Code");
				contract.ChangeOfficeZip(string);
				break;
			case '6':
				int sqFootage;
				sqFootage = GetSquareFootage();
				contract.ChangeSquareFootage(sqFootage);
				break;
			case '7':
				int numDesks;
				numDesks = GetNumDesks();
				contract.ChangeNumDesks(numDesks);
				break;
			case '8':
				int numDays;
				numDays = GetNumDays();
				contract.ChangeNumDays(numDays);
				break;
			default:
				cout << endl << properties[index] << " is an invalid code.\n";

		}  // end switch
		index++;
	} // end while
	clrscr();   // Clear the screen before displaying the new contract
                   // Requires that one include the file 'conio.h'

	DisplayInfo(contract); // allow user to see changes before proceeding
}
The function 'GetProperties' retrieves at once all the properties to be changed and then the loop processing begins. Just for the heck of it, this code is written using a while loop. A 'for' loop would have worked just as well.

We want to begin by looking at the first element of the string array ' properties'. Since a while loop does not have a special mechanism to initialize variables, the line:

int index = 0;

comes right before the loop begins. This variable will be used as an index into the array, allowing the code to examine the individual characters in the string one by one.

Examine the test condition for the while loop:

while (properties[index])

Recall that the character '\0' is equivalent to the integer value 0 and therefore it is equivalent to FALSE. (In contrast, the character '0' has an ASCII code whose integer equivalent is 48!). Therefore, this while loop will continue to execute until the NULL character appears in the string.

Inside the loop is a switch statement that allows the program to perform the appropriate instructions for each of the property codes. For example, if property code 3 appears in the string, the program will call the function 'GetNewString' passing the string "Office City". When 'GetNewString' returns with the new value for the office city, the member function 'ChangeOfficeCity' is called for the contract in question and the value of the office city property is changed. Note that the very last line of the while loop increments the index variable to insure that the program examines the next character in the string.

The last two lines of the function are a bonus. First, the screen is cleared and then the code displays the new property values for the contract. Strictly speaking, this is not part of the interface we have developed, but it does make sense to display the changes in case some mistake was made.

That finishes this discussion except for the code for the function 'GetProperties'. As usual when we use functional decomposition to break our code up into small, manageable functions, the code for this function is relatively simple. The algorithm has two lines:

Display the Menu of Property Codes
Read in the codes entered by the user.

You have written the code to display a menu many times by now and we just saw how to read in a stream of characters (the characters entered by the user) using the 'get' member function. We also saw that it is wise to use the 'ignore' member function before using 'get'. Given all this, the following code should come as no surprise:


void GetProperties(String80 properties)
{  	cin.ignore(80, '\n');
	cout << "Please enter the codes corresponding to the properties you want to "; 
	cout << "change\n";
	cout << "Enter as many as you desire. When finished, hit Enter.\n";

	cout << "          '1':  Contractee Name\n";
	cout << "          '2':  Office Street Address\n";
	cout << "          '3':  Office City\n";
	cout << "          '4':  Office State\n";
	cout << "          '5':  Office Zip\n";
	cout << "          '6':  square Footage\n";
	cout << "          '7':  number of desks\n";
	cout << "          '8':  number of days per week\n";
	cin.get(properties,81);
}
As with 'GetNewString' the user will type any length string he or she desires - representing the codes for as many properties as need to be changed. When finished, the user hits the 'Enter' key, the program stops reading from the input stream and the function returns. Note that since the string value is returned via a parameter, there is no reason for an explicit return statement.

The code to add a new contract is also affected by this new interface, at least by the new string properties. There are more complex ways of modifying the code for this option ( as in this example) but one simple way is simply to write repeated calls to 'GetNewString', passing the appropriate property name string each time. Here then is the code:



void CreateContract(int contractID, Contract& contract)
{  String80 string;
	contract.ChangeID(contractID);

	GetNewString(string, "ContracteeName");
	contract.ChangeContracteeName(string);

	GetNewString(string, "Office Street Address");
	contract.ChangeOfficeStreetAddress(string);

	GetNewString(string, "Office City");
	contract.ChangeOfficeCity(string);

	GetNewString(string, "Office State");
	contract.ChangeOfficeState(string);

	GetNewString(string, "Office Zip Code");
	contract.ChangeOfficeZip(string);

	int sqFootage;
	sqFootage = GetSquareFootage();
	contract.ChangeSquareFootage(sqFootage);

	int numDesks;
	numDesks = GetNumDesks();
	contract.ChangeNumDesks(numDesks);

	int numDays;
	numDays = GetNumDays();
	contract.ChangeNumDays(numDays);

	clrscr();
	DisplayInfo(contract);
}
******************

Changes will also have to be made to the function 'InitializeInstances' to handle the new string properties. These, however, simply involve calls to the new member functions for changing the values of the new properties. The first contract created in the present version of this function has contract ID '1234'. To provide a contractee name for this contract one would simply call the member function 'ChangeContracteeName' with the string constant of the name to be included. The code might be:

contract.ChangeContracteeName("Freddy Smith");

The same approach will work for all the other properties as you can see by examining the code in the file ch10tst1.cpp.

The only other changes required in this program are in the function 'DisplayInfo' - the new string properties need to be output and we earlier decided to streamline the form of that output. Unlike string input and manipulation which required serious modifications to the code, string output is simple. As noted earlier, we have already been doing string output in the form of prompts and other messages. The only difference is that now we are working with string variables instead of string constants. For that reason the code is self explanatory and you should simply review the file ch10tst1.cpp.

After you are comfortable with the code in that file, you should compile, link, and run the complete program. The files you will need are:

That completes this chapter. You have learned a lot in the last two chapters:

    - Pointers and how to use them
    - Strings and how to manipulate and input them
    - The use of pointers and strings in classes
    -Destructors

Probably string handling - inside and outside of classes - was the hardest part of this material. You may be pleased to know (and frustrated if you did not discover sooner) that the newer versions of C++ include a standard string class that builds in many of the details we had to explicitly code. Why weren't you told sooner? Well, what you learned here can be applied to other situations. As a matter of fact, if we were faced with a program, for example, that dealt with multiple different lists and a class that included them all, we would have to write exactly the kind of code you wrote here.

Anyway, if you want to use this new class, there is information in the "C++ Essentials " document and in a document referenced in chapter 2. You should also check out your compiler manual for references to the 'string' class. Once you include the appropriate header file - look for the file name "cstring.h"- you should be able to declare variables of type 'string'. (As you have learned, if the class name is 'string', then there is a new type called 'string'.) Review the public member functions and constructor descriptions in your manual for more information on the many ways variables of this type can be used.
Top of Section Main Menu Next Chapter