| |
Main Menu | Next Section |
Section VI: Declaring the New Contract Class
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:
We have five new properties - all strings. We could have declared
some string type, for example:
and then declared all these data members to be of this type as
in:
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:
the output will be:
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:
This time the output will be:
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:
Then, the output would have been:
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
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:
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:
D. The New Provide and Change Member Functions
Next come the 'Provide...' and 'Change...' member functions for
the five new properties:
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
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
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'.
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:
can be said to mean:
The member function 'CopyContract' might be used as follows:
with the meaning:
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:
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:
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++"
| |
Main Menu | Next Section |