Previous Chapter Main Menu Next Section

CHAPTER 8

AN EVEN BETTER CONTRACT PROGRAM


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 VII: Improving ListOfContracts Section VIII: Rewriting the Nonmember Functions

A. The Need for contract Id's
If Olympia started to use her contract program, she would have two complaints. Her first one would be that she could not save the data at the end of the day and, the second, that she was stuck with whatever number of contracts were written into the program and, therefore, could not add or delete contracts. We noted at the end of the last chapter that the issue of saving data will be covered in chapter 11, when we talk about files. Now that we have learned about arrays, however, we can tackle the second problem.

The solution requires that we first make an array of contracts. In the last chapter we created an array of 10,000 inventory items, where each element of the array represented an existing product. We will start the same way here by creating an array of contracts, where each element of the array represents an existing contract. Later, we will deal with the issue of adding and deleting contracts.

In the inventory program we had a code for each item in the inventory. Our original analysis of Olympia's contract program did not worry about such a code or contract ID, since we only had five contracts. However, if we are going to have many contracts and later be adding and deleting them, we need to have some way of uniquely identifying each contract.

We can use contract ID's to uniquely identify each contract, as we used the ID numbers 1 through 5 in the code we completed in chapter 6 in the file ch6cntr2.cpp. There, we used switch statements to determine which contract to pass as a parameter to the 'DisplayInfo' or 'ChangeInfo' functions. A glance at that code, however, will indicate the problems that would arise if we decided that Olympia was to have 20 or a 100 contracts. For one, our switch statements would get much bigger. The problem is that we have to pass a different variable, depending on which contract we wish to handle. Examine the code carefully to see the truth of this.

In the last chapter we saw that it is possible to have access to the individual elements of an array simply by using the appropriate value for the subscript. For simplicity's sake, we used the item code as the 'appropriate value' for the subscript. In other words, the item code always matched the subsscript value. Note that this was somewhat artificial because in any real environment, item codes are not sequential. The same will also be true for contract ID's, especially when we add the ability to delete contracts. However, for now, we will use contract ID's as subscripts. This will make our code simpler while we focus on other issues.

B. Working with Arrays of Contracts
Our first step in this improved contract handling program is to work on declaring a new array type and variables of that type. The class name used in chapter 6 for contracts was 'Contract', so one might assume that to declare a new type we could simple write:

Good assumption! However, things are a bit more complex when we proceed to declare a variable of this type. Consider the line:

ContractArray contracts;

As an array declaration, it is perfectly legal. However, consider what it does. It creates an array of 100 instances of the class 'Contract'. But, remember, as we have written the constructor for the class 'Contract' , every time an instance is created, the information on square footage, number of desks, and number of days must be provided. That is not being done in this declaration!

It turns out that in C++ it is not possible at array declaration time to provide the necessary property values for each element of an array of class instances. What we need to do is write a constructor that creates instances with default data. The idea is that if an instance being created is not given specific values for its instance variables, then it will use some values likely to be valid or that, at least, make sense as initial values. Notice that this is different from leaving the instance variables filled with garbage. When we use the term 'garbage' for a variable's value, we are referring to some value that was left over from the last time the memory associated with this variable was used. A default value is a value we explicitly put into the memory associated with a variable but which is, at best, a guess at the value that will ultimately go there.

For example, if we are dealing with instances of bank accounts where each instance has one property representing the amount of money in the account and a second property representing the interest rate, we might say that the default values are 0 for the amount and 4% for the interest rate - assuming:

  1. that it is better to start an account with no money in it and adjust the amount later, rather than the other way around and,
  2. that most accounts in this particular bank have a 4% interest rate.

In the case of the Contract class it makes most sense to set the values for the three instance variables to 0 if not told otherwise.

We could accomplish this by simply changing the constructor for the 'Contract' class so that it receives no parameters and sets the values of all properties to 0. This would mean, however, that we could not in the same program instantiate an instance of the class with specific values as we did before. We would be back where we were when we first looked at constructors. If you recall, before we wrote our first constructor, we simply declared our instances as empty and filled them in afterwards using the 'Change...' member functions.

At that time we talked about the system supplied, default constructor. That constructor, supplied by the system when the programmer did not declare one, is different from the new one we want here. That one left the data members filled with garbage. The one proposed here would initialize the data members of ALL instances created with it to some specific set of values - the so-called default values.

C. Function Overloading
What we want is the ability to instantiate instances both with and without specific property values and C++ allows us to do this. It is legal to write two functions (or as many as are needed) that have the same name but have different numbers and/or types of parameters. This is called function overloading. In our case then, we will have two constructors because, as we know, constructors are simply a special kind of function. They are shown below:

Contract(int sqFootage, int numDesks, int numDays);
(the one we have already seen)

and

Contract();

These constructor functions must have the same name because they are both to act as constructors for the "Contract' class and, by definition, a constructor for a class has the same name as the class. In general, the compiler knows which function to use (among a set of functions all with the same name) by looking at the number and type of the actual parameters used when the function is called. In our case, one constructor expects three integer parameters and the other none. If there are three actual parameters in an instantiation of a contract , all of which are integers, the first constructor is called. But, if there are no parameters, the second constructor is called. And, of course, the compiler will generate an error if we try to use a Contract constructor any other way.

Be aware that constructor calls are somewhat implicit. The call takes place when the instance is declared. Consider the instantiation:

Contract c1(300, 10,5);

We know that this means that the instance 'c1' is instantiated with the initial values of 300 for the square footage, 10 for the number of desks, and 5 for the number of days. We can also think of this line of code as a call to the version of the Contract constructor that has three formal parameters. Likewise, if we write:

Contract c2;

we can interpret this as a call to the version of the Contract constructor that has no parameters.

In the case of the array declaration above, 100 instances are declared and therefore 100 calls are made to a constructor for the class 'Contract'. Since each call has no parameters, the constructor with no formal parameters is used. If we want all default property values to be 0, the constructor definition for this second version would look as follows:


You are allowed to overload functions in C++ as long as the there is something different about the parameter lists. The difference can be in terms of the number of parameters in the two functions, in terms of different types of parameters, or in terms of both. Note that one can have different types returned by the two functions, but the compiler does not look at this in deciding which function to use. In other words, if the number and type of parameters are the same but the return types are different, the compiler will have no way to determine which one to call at run time. For example, the declarations:

void Function1(int x, double y);
void Function1(double x, int y);

are valid since the first parameter in one declaration is of type 'int' while in the second declaration it is of type 'double'. On the other hand, the declarations:

void Function1(int x, double y);
char Function1(int x, double y);

are not valid because the only difference between them is in the return type.

Our attention for now will be on constructor overloading. However, overloading in general is a very powerful addition to C++. In addition to function overloading, it is also possible to overload operators such as +, -, and << or >>.

The new 'Contract' class declaration is shown below.

Topics Covered in the "Essentials of C++"
Constructors
Overloading Operators
Overloading Functions
Top of Section Main Menu Next Section