| |
Main Menu | Next Section |
A. Redesigning the Employee Salary Problem
There are many ways to decompose a problem. In the first section
of this chapter we decomposed the Employee Salary problem into
the following modules:
Notice that the two " Get ....:" modules are very similar
and one could easily have seen this as one module:
This module could then have been further decomposed into the two
modules we originally discovered. Our structure chart would then
have looked like:
In some ways this is a better approach since it captures the very
common process of developing a high level decomposition, followed
by a refined decomposition. Unfortunately, this approach can not
be implemented in C++ given what you know. Ask yourself the question,
"What should 'GetEmployeeInformation' return to its calling
function?" The answer is the hours worked and the rate of
pay - two values. As we have seen, however, functions can
only return one value using the normal mechanism. You are about
to learn more about how function parameters work and a bit of
'magic' that will be further explained in chapter 7. Focus on the new
information and just accept the magic for now.
B. How Parameters Work
The variables declared inside the parentheses in a function declaration
are referred to as the formal parameters while the parameters
on the calling side are called the actual parameters. As
an example, let us use the function CalculateSalary. The declaration
for this function was written as:
and therefore 'rate' and 'hours' are formal parameters. On the
calling side, we wrote:
so 'rateOfPay' and 'hoursWorked' are the actual parameters.
The formal parameters we have worked with so far have acted as one way streets or one way communication paths. Information is only passed from the calling function to the called function or from the actual to the formal parameters but not back. In this example a value was assigned to 'rateOfPay' and, likewise, to 'hoursWorked' in 'main'. These variables were then used as actual parameters to pass their values to the formal parameters 'hours' and 'rate'.
When a function has multiple parameters, the first value in the actual parameter list is passed to the first value in the formal parameter list, the second to the second, etc. The names have nothing to do with the way the values are passed. In fact, if we had messed up and written the function call as:
the first actual parameter, 'hoursWorked', would have sent its
value to the first formal parameter, 'rate'. The variable 'rate'
in the function 'CalculateSalary' would have held the hours worked!
OOPS!!! Of course, 'hours' would have held the rate of pay - for
a double OOPS! The moral of the story: Don't think that the compiler
cares a bit about the names you give to your parameters.
You must be careful then with the way you code parameters. In this case, you would have received no error message from the compiler but your program sure would have provided funny answers. Another reason to
your code.
As usual, we got side tracked. The original question concerned the possibility of declaring a function such as 'GetEmployeeInformation' when we know that it needs to return two values, in this case, the rate of pay and the hours worked. As we noted, the actual/formal parameter system we have been working with acts as a one way communication path - from actual to formal. Remember that formal parameters are actually local variables, known only to the function they are declared in. This means that each local variable has its own memory location and any changes to that memory location have no impact on a corresponding variable acting as an actual parameter. To concretize this, suppose we rewrote the function declaration for CalculateSalary so that the formal parameter names matched the actual parameter names in the call in 'main'. The call would be the same as it was:
and the declaration would be:
If we assume that the user entered the values 6.25 for the 'rateOfPay'
and 38 for 'hoursWorked' the following might be a stylized view
of memory after the parameter values were passed but before any
of the code in the function had executed.

Note that there are two memory locations labeled 'rateOfPay' and
two labeled 'hoursWorked' - one set for 'main' and one for 'CalculateSalary'.
Then, suppose that for some, silly reason we include the line:
inside the function definition for 'CalculateSalary'. When this
line was executed, memory would look as follows:

The interesting thing is that the 'local' version of 'rateOfPay' gets this new value BUT the variable with the same name in 'main' retains its old value. In other words, the code we write in the function 'CalculateSalary' has no effect on the variables in 'main'.
What you need to understand is that, given what you have seen so far, there is no way to change a variable contained in one function through an instruction executed in another function - except via the 'return' instruction. And, since the 'return' instruction only allows one value to be returned, we seem to be stuck with regard to the 'GetEmployeeInformation' function.
There is, however, a solution. If we rewrite the function declaration
above as:
(note the ampersand after the word 'double' ), the picture of
memory changes. Now, and here is the magic that we won't explain for now, there is only one memory location labeled 'rateOfPay' and it is used by both 'main' and 'Calculate Salary'.

Now, if again we include the same instruction
inside the function definition, the variable in both places (the
functions CalculateSalary and 'main') has its value changed.
As a matter of fact, as we just noted, there are not two variables - two separate memory locations with the symbolic name 'rateOfPay' - but one.
Further, if we go back to the original function declaration (where the first parameter has the symbolic name 'rate' instead of 'rateOfPay') and
add the ampersand:
there is still only one memory location for the rate of pay. It
simply has two symbolic names (like nicknames) for that one memory
location.

Therefore, if we include the line:
in the function definition, the result is that 'rate' in 'CalculateSalary'
and 'rateOfPay' in 'main' both contain the same value, 3.3! In
other words, the ampersand forces the first parameter pair (formal
and actual) to act as a two-way street. Information can move both
ways. (As we said earlier, for now treat this as magic unless
you want to jump ahead to chapter 7.
D. Declaring 'GetEmployeeInformation' to Return Two Values
Now we see how we can have 'GetEmployeeInformation' return two
pieces of data. Technically, one can return two values by having one value returned via the 'return' instruction and the second returned via a formal parameter declared with an ampersand. Many software engineers, however, don't approve of this. They feel that when you are returning two values of the same kind as in two pieces of information about an employee you should declare two formal parameters with the ampersand and declare the function itself with 'void' for the return type. That is what we will do with the function 'GetEmployeeInformation'.
We declare the function with two formal parameters,
each with an ampersand following the type name, as in:
Since the 'return' mechanism is not used, the function return
type is 'void' The ampersands tell the system to treat both formal/actual
parameter pairs as two-way streets, allowing information to pass
both ways. Actually, since 'GetEmployeeInformation' does not receive
any information we don't need to send any information to the function
but it is not possible in C++ to set up a one way only heading
back from the function. We have a two way street where in one
direction garbage is sent. (See the commentary below the following
code for further discussion of this 'garbage'.)
To finish up this process, here is the code - first, the segment
of the code for 'main' that calls this function and second the
code for the definition for this function:
for (int count = 0; count < num_times; count++)
{ GetEmployeeInformation(rateOfPay, hoursWorked);
salary="CalculateSalary(rateOfPay," hoursWorked);
OutputSalary(salary);
}
void GetEmployeeInformation(double& rate, double& hours)
{ rate="GetRateOfPay();" hours="GetHoursWorked();"
}
Note that 'GetEmployeeInformation' does not care how the information is retrieved from the two 'Get....' functions, just that it is retrieved. At the other end, 'main' calls GetEmployeeInformation over and over inside the 'for' loop, not caring how 'GetEmployeeInformation' performs its task.
The first time through the
loop the variables 'rateOfPay' and 'hoursWorked' have garbage
in them that is passed to 'GetEmployeeInformation'. This means
that the variables 'rate' and 'hours' also contain garbage when
'GetEmployeeInformation' is called since they are nicknames for
'rateOfPay' and 'hoursWorked' . This is not a problem, however,
because these garbage values are never used. The code says that
'rate' and 'hours' are simply given new values which, in turn,
means that 'rateOfPay' and 'hoursWorked' also have new values.
On each subsequent pass through the loop, 'rateOfPay' and 'hoursWorked'
have the values for the previous employee and this information
is passed to 'GetEmployeeInformation'. Again, however, this is
irrelevant because this information is not use by 'GetEmployeeInformation'
and the memory locations holding this information immediately
have the information for the next employee placed in them.
If this is not clear, consider the following, perhaps slightly
stretched, example to clarify the situation. Imagine that you
are employed by someone to gather information on students. The
person who has hired you wants to gather information that she
then uses in some way before throwing it away. The person gives
you a box into which you are to put some papers containing the
information on the first student. The box has some garbage in
it which you throw away and replace with the papers with the
information on the first student. You give the box back to the person who hired you,
she looks at it, does something with the information, and then passes you back
the box for the next student. The box still contains the information
from the previous student so you throw it away, gather the information
on the next student and again return the box. This continues some
number of times until the person who hired you is finished.
A program that uses the function GetEmployeeInformation can be found in the file ch4prg2.cpp. Be sure you see how this example relates to the interplay between
'main' and 'GetEmployeeInformation'.
| |
Main Menu | Next Section |