CHAPTER 10
|
Table of Contents
Learning C++:
An Index of Entry Points
2. The A reference document on the basic elements of C++.
3. The Patterns
|
A. The New 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
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:
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: 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 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
char* ProvideOfficeStreetAddress(); char* ProvideOfficeCity(); char* ProvideOfficeState(); char* ProvideOfficeZip(); 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
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++" |
|