CHAPTER 10
| 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 A reference document on the basic elements of C++.
3. The Patterns
|
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:
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:
If one wanted completely unique prompts, one could pass in the whole
prompt and rewrite the cout line simply as:
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:
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: 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:
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:
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:
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: 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: 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:
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:
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. |