cs1ch10sec6.htm
 CHAPTER 10

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

Section VI: Declaring the New Contract Class 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 VII: Defining the New Contract Class Section VIII: Modifying the Main Program

  


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





A. The New Properties
The analysis above indicated that we needed to add a number of new data members to the 'Contract' class. All of these are to be string variables. Each of these data members must also have 'Change...' and 'Provide...' member functions. Here are the changes to the class declaration starting with the declarations for the properties:

private:

int contractID;
int squareFootage;
int numberOfDaysPerWeek;
int numberOfDesks;

char* contracteeName;

char* officeStreetAddress;
// most complete addresses include space for a 2nd street address
// We will skip it here for simplicity

char* officeCity;
char* officeState;
char* officeZip;

We have five new properties - all strings. We could have declared some string type, for example:

typedef char String50[51];

and then declared all these data members to be of this type as in:

String50 contracteeName;

However, we have no idea how long these strings will be. Since Olympia does not use some application form that limits the size of the name or other properties (We, of course, checked with her on this back in the analysis phase.), it is safer to declare our strings with no limit on size. By declaring the properties as char pointers, we are not committing ourselves to any length. Of course, later we will need to request access memory for each of these strings, but that is part of the definition so it can be ignored for now.

Notice how the address has been broken up into four separate strings. As already discussed, this will allow a user program to access parts of the address. For example, some later program might need to access all contracts on "8th Street" for a special promotion.

Since the data member names are well chosen, we do not need comments with them. The one comment in this piece of code points out that there really should be a second street address field. For example, business addresses often include a department name or mail stop or room number in addition to a street address for the building. Another addition in this day of international business might be a Country name - although in an office cleaning program this might only make sense if Olympia lived in a border town. Anyway, all these details were ignored to keep the program simple. Once we know how to work with string types in general, adding more string variables is not a problem.


B. Changes to the Contract Class Declaration - the Constructors
Now we can look at the constructor declarations:


// Declaration of the class Contract - with a small set of strings
// File: contrct6.h

#ifndef CONTRACT_H
#define CONTRACT_H

class Contract
	{public:
		 Contract(int ID, int sqFootage, int numDesks, int numDays,
			      char* name,
			      char* oStreetAddress = "",   char* oCity = "",
			      char* oState = "",   char* oZip = "");
		/*
		Purpose:	To instantiate (create) an instance of the class 'contract'.
		Goal:		A new instance of class 'contract' now exists

		Receives:	ID, Square Footage, Number of Desks, Number of Days 
					(All Integers)

				name, oStreetAddress, oCity, oState, oZip, oCountry 
					(All Strings)

		Returns:	 NONE
		*/


		Contract();
		/*
		Purpose:To instantiate (create) an instance of the class 'contract' with
					default values.
		Goal:		A new instance of class 'contract' now exists

		Receives:	NONE
		Returns:	NONE
		*/

		Contract(const Contract& c);
		/*
		Purpose:  To instantiate (create) an instance of the class 'contract',copying
					the values from another contract
		Goal:		A new instance of class 'contract' now exists

		Receives:	A contract
		Returns:	NONE
		*/

The first constructor introduces a new aspect to parameter declarations. As we hinted in chapter 9, Section IV, when we discussed the member function 'get', it is possible to provide default values to function parameters in the parameter list itself. to parameters in the parameter list itself. This is true for all functions, not just constructors, although it is the primary way this feature will be used here. If a value is not passed for a parameter and it has a default value, the parameter will use the default value anywhere the parameter is used in the function definition. Consider the following code fragment:


	// function declaration
	void SomeFunction(int amount, char* name = "Unknown",
				double cost = 10.00); 	  

	// function definition
	void SomeFunction(int var1, char* name, double cost)	 
	{	cout << name << " bought " << amount  << " items for " << price;
	}
While this code has no real purpose, it is a good example of the use of default parameter values. The declaration includes default values for the second and third parameters. As you can see, this is accomplished by what looks like an assignment statement. Note that the function definition does not include the default values. Usually the first line of a definition looks the same as the declaration. Default values, however, are NOT included in the definition.

If this function is called with the line:

SomeFunction(3);

the output will be:

Unknown bought 3 items for 10.00

Since no value was passed for the parameters 'name' and 'cost', the default values were used. It would also be possible to have the following call:

SomeFunction(3, "Fred");

This time the output will be:

Fred bought 3 items for 10.00

Since the value 'Fred' was passed to the 'name' parameter, the default value of 'Unknown' is not used. The default value for 'cost' is, however, still used.

Of course, the function call could have included values for all three parameters, as in:

SomeFunction(3, "Fred", 19.95);

Then, the output would have been:

Fred bought 3 items for 19.95

There are a number of restrictions on the use of default parameters based on the rules for how formal and actual parameters match up. As you may recall, the first actual parameter always becomes the value for the first formal parameter, the second for the second etc. Thus it is not possible in a call to 'SomeFunction' to provide a value for 'cost' and not provide a value for 'name'. Try to explain this to yourself before reading the next paragraph.

Consider the function call

SomeFunction(3, 12.95);

and assume that the value 12.95 is meant to be the cost of the items purchased. By the rule above, however, the system will attempt to place the value '12.95' in the formal parameter 'name' since '12.95' is the second actual parameter and 'name' is the second formal parameter. Obviously, 12.95 is not a name and C++ won't allow it anyway because 12.95 is not a string and 'name' expects a string value. In this case we are lucky because we will get an error message warning us of the problem. If the second formal parameter had also been of type double, we would not have gotten an error message and would not have noticed any problem until we carefully examined the output. (Of course, we always carefully examine the output of our test runs and some of those runs will include tests of any default parameter values.)

On the declaration side it would not be legal to write:

 
     void SomeFunction(double cost = 10.00, char* name = "Unknown"
		       int amount); 
This declaration says that it is possible to leave out the cost and the name values in the parameters of a function call while passing an amount. However, consider the call:

SomeFunction(3);

where we want '3' to the the amount. If this code were legal, the system would copy the '3' to the parameter 'cost' (first to first) and would not have a value for 'amount' which does not have a default value. The end result would be the same type of error message you have most likely already seen when you failed to have the same number of formal and actual parameters and there were no default values. (In Borland C++ the error is "Too few parameters in call to …..")

The conclusion of this is that default parameters make certain function declarations possible but one must design the declaration carefully. All required parameters (those without defaults) must come first and those parameters with defaults must be ordered so that those least likely to be included in function calls come at the end of the parameter list.

In the case of the first constructor declaration for the 'Contract' class:

 
	Contract(int ID, int sqFootage, int numDesks, int numDays,
			char* name,
			char* oStreetAddress = "", char* oCity = "",
			char* oStat = "", char* oZip = "");
we have nine parameters - as programs become more complex, parameter lists can grow, especially in constructors. According to the program narrative, whenever a contract is created, the ID, square footage, number of desks, number of days, and the contractee's name must be provided. Therefore, these parameters do not have defaults. By the same token, the other properties do not need to be provided immediately so default values are provided for these. The default value chosen was a blank. In other words, these will be empty strings, if no other information is provided. Note that it would not be possible to instantiate a contract where, for example, the zip code is provided but not the street address.

The second constructor declaration does not change. Technically, however, we could get rid of it completely if we rewrite the first constructor to include default values for ALL parameters. A function with default values for all parameters can be called with no actual parameters which, if you recall, was the original purpose of this second constructor. However, that would extend the 'power' of the first constructor past where we want and lose the distinction between those properties that must be provided immediately when a new contract is created and those that can wait as stated in the problem specifications.

There is a third constructor here, the so called copy constructor. This will be discussed in more detail below (in part I\E).

C. Changes to the Contract Class Declaration - the Destructor
The next change in the class declaration is the addition of a destructor. We noted above that a destructor was required for any class that includes pointers to arrays. More precisely, a destructor is needed for any class that includes any kind of pointer. Since strings in the 'Contract class are character pointers, we need a destructor for this class.

As we saw, it is easy to declare a destructor: one simply use the Class name preceded by a tilde. Thus the 'Contract' class destructor is declared as follows:

Contract:: ~Contract(); // destructor

D. The New Provide and Change Member Functions
Next come the 'Provide...' and 'Change...' member functions for the five new properties:

    char* ProvideContracteeName();
    void ChangeContracteeName(char* name);

    char* ProvideOfficeStreetAddress();
    void ChangeOfficeStreetAddress(char* streetAddr);

    char* ProvideOfficeCity();
    void ChangeOfficeCity(char* city);

    char* ProvideOfficeState();
    void ChangeOfficeState(char* state);

    char* ProvideOfficeZip();
    void ChangeOfficeZip(char* zip);

They are all essentially the same as other such functions. The 'Provide...' functions do not receive anything, so they have no parameters and return the type of the property they refer to. In this case, they all return strings. The 'Change...' functions all receive a string - the new value of the property they refer to - and return nothing. As with the other string declarations in this file, all the strings here are declared as pointers to chars.

E. Special Member Functions Required to Handle Pointers
Finally, because we are implementing the strings with pointers, we need a new type of member function and a new variation of the constructor. Consider the following function definition:

 
	void SomeFunction(Contract c1 Contract c2);
	{	
		c2 = c1;
		// other processing
	}

We are not interested in why c1 is copied to c2 (via the assignment statement) but just that it is. When contracts did not include strings or other pointer type data members, this assignment worked just fine without any special handling. The square footage, number of desks, etc would get copied from 'c1' to 'c2'.

Exactly the same thing happens when data members include pointers, only it is probably not what we want to happen! Since the data members that are pointers contain addresses, what gets copied is the addresses. In other words, both instances now point to the same memory for the strings, and if the string is changed in one instance, it is effectively changed in the other. Worse, if the class destructor is called for one of the instances (for example, because the instance is represented by a local variable), the memory used by its string data members is returned to the system, even while that same memory is still being used by the other instance. Thus, the memory is both being used and is marked as available for other purposes. Later that returned memory could be requested and used for some completely different purpose - even while it is still being used by the still existing instance.

So, we need a special way to copy classes, such as 'Contract', that have pointer-based data members. For that reason, the following function declaration exists:

void CopyContract(Contract c);

Note that this function explicitly refers to only one contract. Remember that all member functions can be considered to be messages. In other words the code:

    Contract contract1
    contract1.ProvideNumDesks();

can be said to mean:

    Send the message 'ProvideNumDesks' to the contract 'contract1'.

The member function 'CopyContract' might be used as follows:

c2. CopyContract(c1);

with the meaning:

    Send the message CopyContract to c2 - telling it that it should be ready to receive a copy of c1.

The code for 'CopyContract' will make sure that the instance c2 uses separate memory for its strings.

********************

In addition to the 'CopyContract' function, we also need a new constructor - and for the same reason. When an instance of a class is passed by value, it is copied. That is what "pass by value" means. Thus, if we have the function declaration and call:

    void SomeFunction(Contract c1); // the declaration

    SomeFunction(c2); // the call

the instance c2 is copied to c1. But, we just saw that the copy operation has problems. Moreover, we can't handle these problems with the 'CopyContract' function we just declared, since the copy that takes place in the function call here is implicit in the function call and is not something we have control over. In other words, there is no way to call 'CopyContract' to copy the parameter being passed.

To handle this, C++ expects us to declare and define what is called the Copy Constructor:

Contract(const Contract& c);

It is no different from the other constructors we have declared except that it's sole parameter is a contract. This parameter represents the copy to be made when a function call passes an instance of 'Contract' by value. Note that the contract to be copied is passed by reference. If we remove the '&' symbol we are back to pass by value and we have not avoided any of the problems we are trying to deal with here.

When a function call passes a instance of 'Contract' by value, this constructor is called by the system. This constructor receives the contract (called 'c' here) being passed by value and instantiates a copy of that contract to be used as the local parameter.

At this point, you may be a bit confused because you can't imagine what these two functions actually do. This is one time when the 'how' might clarify the 'what'. If you are confused, you might just wait until after you read the discussion on 'how' in the next section.

That completes the discussion of changes to the Contract class declaration. The full code for the declarations is in the file "contrct6.h"

Topics Covered in the "Essentials of C++"

Default Parameters
The Copy Constructor

Top of Section Main Menu Next Section