|
Table of Contents
1. The Story
of C++! (The basics for any beginner!)
2. The A reference document on the basic elements of C++.
3. The Patterns
|
A. Typedef's and Arrays
The names for these three functions might be 'InitializeInventory',
'ReadInInventory', and OutputInventory'. We will explore 'OutputInventory'
first. As usual when working with a new function we start with
the design:
OutputInventory
What does this function need if anything from the calling function
and what does it return? As with all output functions, this one
returns nothing but it needs the data that it is to output. We
could explicitly pass each individual element and have 10,000
parameters! Or, we could pass the array. Here we see another advantage
to giving a group name for all 10,000 items. We can pass the group
name and the system knows to pass all 10,000 elements. Our 'receives'
and 'returns' analysis then looks as follows:
Receives: The inventory of items
As we move on to declare this function we need to ask what is
the type of this array? Careful! The type of the elements of the
array is integer but the array itself is NOT an integer. To make
this clear consider this analogy. Each of the students on a class
roster is a person (we presume!) but the roster itself is not
a person.
There is no 'array' type in C++ and even if there were it would
not help since the type we want here refers not just to any array
but to an array of 10,000 integers. It is time to use the 'typedef'
statement we learned in the previous chapter. At that time we
declared a new type 'Boolean' with the declaration going into
a separate file because we anticipated using that type in many
programs and wanted to be able to repeatedly take advantage of
the type declaration without re-writing it. In this case, the
specific array type we are working with is unlikely to be found
in too many programs so it should not be coded in a separate file.
Instead we put it at the top of the file containing 'main':
// Program to handle inventory for a small company
#include <iostream.h>
typedef int InventoryArray[10000];
void main()
What the 'typedef' line does here is declare the existence of
a new type called 'InventoryArray' and say that any variable of
this type is an array of 10,000 integers. Remember that a type
is not a variable so what the typedef is doing is describing a
new type of 'thing' and giving that type a name. It is not setting
aside any specific memory. Inside 'main' the line
declares the existence of the variable 'itemInventory' of the
type 'InventoryArray' and since 'InventoryArray' is the name for
an array of 10,000 integers, 'itemInventory' is an array of 10,000
integers.
With this, we now also have a type name for the type of the data
to be received by 'OutputInventory'. Here is the finished declaration:
We again use the type name 'InventoryArray'. This indicates that
the array will receive an array of 10,000 integers - which is
just what we want. The local name for this array was arbitrarily
chosen to be 'items'. We learned earlier that the name does not
matter. We could have used the same name, 'itemInventory', that we used in 'main' but
that sometimes is confusing. When we write the function definition,
however, we need to use whatever name we placed in the parameter
list. Therefore, if the function definition parameter is 'items',
the rest of the code better use the variable 'items':
Here is how the whole program looks so far:
The typedef comes before the function declaration so that the
compiler knows about the new type, 'InventoryArray', before seeing
it used. Later in 'main' the function 'OutputInventory' is given
the array of inventory items which it needs. Of course, in a complete
program there had better be some way of getting information into
the array before printing out the information. Otherwise garbage
will be printed. But, we will get there.
B. Globals
In contrast, all the variables we have used in our programs have
been declared either as parameters or inside a function and we
have called them local variables. By local we mean that
they are known only inside the function(s) they are declared in.
Thus, we could declare MAX_ITEMS as a constant inside 'main' and
add it as a parameter to 'OutputInventory'. After all, it is true
that 'OutputInventory' does need it to accomplish its work.
This approach is sometimes used but, more often, a different approach
is taken. It is possible to declare a constant (or a variable)
as global. This means that it is known in ALL functions
that occur in the same file after the declaration. More precisely,
a global constant (or variable) is declared outside all functions
and classes and is known from the point of declaration to the
end of the file. So, if we declare MAX_ITEMS at the top of the
file containing 'main', it will be known to all functions in the
file including 'OutputInventory'. In our case, we will put it
just before the typedef statement and change the typedef to take
advantage of it.:
If you were reading these last paragraphs carefully, you may be
asking yourself, "If variables can be declared as globals
and therefore be known throughout the whole program, why have
parameters - after all, parameters can be a real hassle!?!"
It is true that you can do this but remember all we said about
encapsulation. Using global variables is an invitation
to buggy code. Don't do it! In fact, your instructor is likely
to deduct points from your assignments if he or she finds you
using global variables. There are certain controlled circumstances
when they can and should be used but you have not seen any yet.
By the way, the typedef statement also creates a global type -
'InventoryArray'. We could have declared this new type inside
'main' but then it would have been unavailable outside of 'main'.
In fact, it is very rare to use local typedef's.
C. Array Elements as Parameters
The call to this function would be a bit different in that it
would be passing one element of the array. Here are the relevant
lines of code for 'main':
Note that the first actual parameter in the call to 'OutputOneInventoryAmount' - itemInventory[itemCode] - is a single element of the array. In other words, since array elements of type integer can be used wherever integer variables are used, they can be used as parameters. (The same is true for arrays of all other types.)
The code for 'OutputInventory' in the previous section received
the whole array and included a 'for' statement to go through the
array elements one at a time. This is the most efficient way to
handle the option of outputting the whole array but we could also
have accomplished the same result by using the function we just
wrote, 'OutputOneInventoryAmount', and rewriting the calling side
as:
This would cause the individual amounts to be passed to the output
function. It is not the best code because every function call
takes a bit of time - just as it takes slightly longer to get
someone else to start a task than to it takes to start it yourself.
This last bit of code makes 10,000 simple requests (function calls)
as opposed to one more complex request.
VI. Arrays as Function Parameters to Return Data
InputInventory
Receives: NONE
Ordinarily a function with this kind of a design analysis would
have no parameters and would return one item. However, functions
in C++ cannot return arrays. This means we have to use our other
mechanism for returning values - the parameter list.
Earlier we made a distinction between function parameters declared
with the '&' symbol and those without. We said that formal
parameters
with
the '&' symbol can return information because a 'two way path'
has been created while formal parameters without the '&' symbol
could only be used to transmit information into the function but
not back. It is time to say more about what the '&' operator does to
cause this difference.
When a formal parameter does not have the '&' symbol, the
system makes a copy of the variable being passed and the function
uses that copy. Consider the following code segment:
When the system calls 'DemoFunction' from inside 'main', it already
has set aside memory for a variable called 'def'. And, since
the function declaration for 'DemoFunction' declares 'abc' as
a formal parameter without a '&' symbol, the system, when
the call actually takes place, sets aside enough memory for a
second integer, calls that memory location 'abc', and copies the
contents of 'def' to 'abc'. Thus any changes made to 'abc' in
the function 'DemoFunction' have no effect on the contents of
'def'. When 'DemoFunction' finishes operation and control is returned
to 'main', the memory for 'abc' is returned to the system and
the variable itself no longer exists.
Figure 7.2
Even if both variables had been called 'abc' the result would
be the same. The system would start by setting aside the memory
location for the 'abc' in 'main'. When 'DemoFunction' was called,
the system would then set aside a second memory location for
the 'abc' found in 'DemoFunction' and copy the value found in
the first 'abc' to the second. The system would know to use the
'abc' for 'DemoFunction' as long as 'DemoFunction' was executing.
Again, when 'DemoFunction' completed, its 'abc' variable would
disappear and so would any changes made to its contents.
Figure 7.3
Technically, this parameter passing process is called pass
by value. The value found in the memory location for the actual
parameter is passed to the copy. When the formal parameter is
associated with the '&' symbol, a process called pass by
reference
is used. In this process, the address of
the memory symbolized by the actual parameter is passed. (Memory
addresses are sometimes referred to as references thus the name
'pass by reference'.) Since the function receives and uses the
address of the actual parameter, it works with the actual parameter's
memory and not a copy. Any changes made in the function are made
in the original.
Again, this process works whether or not the actual and formal
parameters have the same name. The variable name used in the formal
parameter becomes a 'nickname' for the memory location named in
the actual parameter. Both the actual and formal parameter names
are symbols referring to the same memory location.
Consider our same example but with the '&' symbol added to
the function declaration:
Now, when 'def' is passed as a parameter, the system does not
make a copy of the contents of 'def'. Instead, 'abc' becomes a
second name for the same memory location and any changes made
to 'abc' are made to 'def'. When 'DemoFunction' completes, 'abc'
still disappears as a variable name but the memory it refers to
is still available as 'def'. And, any changes made to 'abc' still
exist because they were made to the same memory referred to as
'def'.
Figure 7.4
What all this has to do with arrays as parameters is that arrays
are AUTOMATICALLY passed by reference. In other words, any time
an array is used as a parameter, the 'two way path' is created.
There is no need to (and it is poor style to) use the '&'
symbol. Thus, the function declaration for 'InputInventory' has
the same form as 'OutputInventory':
The code to call this function would look the same as the code
to call 'OutputInventory'. And, the code in the definition would
be the same 'for' statement we wrote above for inputting all the
product inventory amounts at once:
One of the reasons for having parameters that only passed information
into functions was to control the change possible. In other words,
if it was impossible to change the value of the actual parameter,
the code could do whatever it wanted to the formal parameter without
worrying about the effect on the rest of the program. With arrays,
however, since they are automatically passed by reference, this
safeguard is not available. But, if you wish to protect an array
from being changed, you can add the word 'const' in front of the
parameter declaration in the function declaration. For example,
it would be better then to write the declaration for 'OutputInventory'
as:
Note that we would not want to do this for 'InputInventory' because
we ARE changing the values in the array elements.
Click here for the complete code
for this problem - with a menu added to allow the user to do as
he or she wishes. To make the program easier to test, the number
of inventory items has been reduced to 5. This allows the user
to enter the inventory information without a lot of effort. As
we have already said when talking about program testing - it is
important to design one's tests so that they focus on the real
issues and don't take unnecessary time or energy. And, by the
way, this demonstrates the value in using constants. No change
needs to be made between the test and 'full version' of this program
except for the value of MAX_ITEMS.
There is one element that
makes this program highly artificial. In any real program of this kind,
the user would be allowed to save the inventory data to a file at the end
of the day and, at the start of next day's business, read that information
back into the array. In chapter 10, we will look at file handling and fix
this detail. STAY TUNED! |
|