home *** CD-ROM | disk | FTP | other *** search
-
-
-
- Chapter 4
- FUNCTIONS
-
- This chapter discusses enhancements in the capabilities of
- functions that have been made to C++. These changes make
- programming more convenient and permit the compiler to do further
- checking for errors. A fair amount of time is also spent in this
- chapter teaching the modern form of function definition and
- prototyping.
-
- Prototyping allows the compiler to do additional type checking
- for your function calls which can detect some programming errors.
- The first two example programs in this chapter are designed to
- teach prototyping and what it will do for you. Prototyping is a
- relatively new addition to C, so even some experienced C
- programmers are not familiar with it. If you have experience
- with prototyping you can skip directly to the section named PASS
- BY REFERENCE on page 4-4 of this chapter.
-
-
- PROTOTYPES
- -----------------------------------------------------------------
- Examine the file named PROTYPE1.CPP for our ==================
- first look at a prototype and an illustration PROTYPE1.CPP
- of how it is used. The prototyping used in ==================
- C++ is no different than that used in ANSI-C.
- Actually, many C programmers take a rather dim view of
- prototyping and seem reluctant to use it, but with C++ it is
- considerably more important and is in much heavier use. In fact,
- prototyping is required to be used in some situations in C++.
-
- A prototype is a limited model of a more complete entity to come
- later. In this case, the full function is the complete entity to
- come later and the prototype is illustrated in line 4. The
- prototype gives a model of the interface to the function that can
- be used to check the calls to the function for the proper number
- of parameters and the correct types of parameters. Each call to
- the function named do_stuff() must have exactly three parameters
- or the compiler will give an error message. In addition to the
- correct number of parameters, the types must be compatible or the
- compiler will issue an error message. Notice that when the
- compiler is working on lines 12 and 13, the type checking can be
- done based on the prototype in line 4 even though the function
- itself is not yet defined. If the prototype is not given, the
- number of parameters will not be checked, nor will the types of
- the parameters be checked. Without a prototype, if you have the
- wrong number of parameters, you will get an apparently good
- compile and link, but the program may do some very strange things
- when it is executed.
-
- To write the prototype, simply copy the header from the function
- to the beginning of the program and append a semicolon to the end
-
- Page 4-1
-
- Chapter 4 - Functions
-
- as a signal to the compiler that this is not a function but a
- prototype. The variable names given in the prototype are
- optional and act merely as comments to the program reader since
- they are completely ignored by the compiler. You could replace
- the variable name wings in line 4 with your first name and there
- would be no difference in compilation. Of course, the next
- person that had to read your program would be somewhat baffled
- with your choice of variable names.
-
- In this case, the two function calls to this function, given in
- lines 12 and 13, are correct so no error will be listed during
- compilation.
-
- Even though we wish to use the char type for eyes in the
- function, we wish to use it as a number rather than as a
- character. The cast to int in line 20 is required to force the
- printout of the numerical value rather than an ASCII character.
- The next example program is similar but without the cast to int.
-
-
- COMPATIBLE TYPES
- -----------------------------------------------------------------
- We mentioned compatible types earlier so we should review them
- just a bit in order to make our discussion of prototyping
- complete. Compatible types are any simple types that can be
- converted from one to another in a meaningful way. For example,
- if you used an integer as the actual parameter and the function
- was expecting a float type as the formal parameter, the system
- would do the conversion automatically, without mentioning it to
- you. This is also true of a float changing to a char, or a char
- changing to an int. There are definite conversion rules which
- would be followed. These rules are given in great detail in
- section 3.2 of the draft of the ANSI-C standard and are also
- given on page 198 of the second edition of the K&R reference.
-
- If we supplied a pointer to an integer as the actual parameter
- and expected an integer as the formal parameter in the function,
- the conversion would not be made because they are two entirely
- different kinds of values. Likewise, a structure would not be
- converted automatically to a long float, an array, or even to a
- different kind of structure, because they are all incompatible
- and cannot be converted in any meaningful manner. The entire
- issue of type compatibility as discussed in chapter 2 of this
- tutorial applies equally well to the compatibility of types when c
- alling a function. Likewise, the type specified as the return
- type, in this case void, must be compatible with the expected
- return type in the calling statement, or the compiler will issue
- a warning.
-
- HOW DOES PROTOTYPING WORK?
- -----------------------------------------------------------------
- This is your chance to try prototyping for yourself and see how
- well it works and what kinds of error messages you get when you
-
- Page 4-2
-
- Chapter 4 - Functions
-
- do certain wrong things. Change the actual parameters in line 12
- to read (12.2, 13, 12345) and see what the compiler says about
- that change. It will probably say nothing because they are all
- type compatible. If you change it to read (12.0, 13), it will
- issue a warning or error because there are not enough arguments
- given. Likewise you should receive an error message if you
- change one of the parameters in line 13 to an address by putting
- an ampersand in front of one of the variable names. Finally,
- change the first word in line 4 from void to int and see what
- kind of error message is given. You will first be required to
- make the function header in line 16 agree with the prototype,
- then you will find that there is not a variable returned from
- the function. You should have a good feeling that prototyping
- is doing something worthwhile for you after making these changes.
-
- Be sure to compile and execute this program then make the changes
- recommended above, attempting to compile it after each change.
-
-
- A LITTLE MORE PROTOTYPING
- -----------------------------------------------------------------
- Examine the next example program named ==================
- PROTYPE2.CPP for a little more information on PROTYPE2.CPP
- prototyping. This program is identical to ==================
- the last one except for a few small changes.
- The variable names have been omitted from the prototype in line 4
- merely as an illustration that they are interpreted as comments
- by the C++ compiler. The function header is formatted
- differently to allow for a comment alongside each of the actual
- parameters. This should make the function header a little more
- self explanatory. However, you should remember that comments
- should not be used to replace careful selection of variable
- names. In this particular case, the comments add essentially
- nothing to the clarity of the program.
-
-
- WHAT DOES PROTOTYPING COST?
- -----------------------------------------------------------------
- Prototyping is essentially free because it costs absolutely
- nothing concerning the run time size or speed of execution.
- Prototyping is a compile time check only, and slows down the
- compile time a negligible amount because of the extra checking
- that the compiler must do. If prototyping finds one error for
- you that you would have had to find with a debugger, it has more
- than paid for itself for use in an entire project. I once spent
- 12 hours of debugging time to find that I forgot to pass the
- address of a variable to a function. Prototyping would have
- found the error on the first compilation of that 2000 line
- program.
-
- The only price you pay to use prototyping is the extra size of
- the source files because of the prototypes, and the extra time
-
-
- Page 4-3
-
- Chapter 4 - Functions
-
- for the compiler to read the prototypes during the compilation
- process, but both costs are negligible.
-
- Be sure to compile and execute this example program. You will
- find that it is identical to the last example program, except
- for the changes in the prototype and the removal of the cast
- from the last line of the function.
-
-
- PASS BY REFERENCE
- -----------------------------------------------------------------
- Examine the program named PASSREF.CPP for an =================
- example of a pass by reference, a construct PASSREF.CPP
- which is not available in ANSI-C. The =================
- reference variable was mentioned in chapter 1
- and it was recommended there that you don't use it in the manner
- illustrated there. This example program illustrates a situation
- where it can be used to your advantage. The pass by reference
- allows the passing of a variable to a function and returning the
- changes made in the function to the main program. In ANSI-C the
- same effect can be seen when a pointer to a variable is passed
- to a function, but use of a reference variable is a little
- cleaner.
-
- Observe the prototype in line 4 where the second variable has an
- ampersand in front of the variable name. The ampersand instructs
- the compiler to treat this variable just like it were passed a
- pointer to the actual variable passed from the calling function.
- It acts like the actual variable from the main program is used in
- the function. In the function itself, in lines 22 through 25,
- the variable in2 is used just like any other variable but it acts
- like we are using the variable passed to this function from the
- main program not a copy of it. The other variable named in1 is
- treated just like any other normal variable in ANSI-C. In
- effect, the name in2 is a synonym for the variable named index in
- the main program, but the name in1 refers to a copy of the
- variable count from the main program. In actual practice, a
- pointer is passed to the function and it is automatically
- dereferenced when used in the function. This is transparent to
- you, the programmer.
-
- If you prefer to omit the variable names in the prototypes, you
- would write the prototype as follows;
-
- void fiddle(int, int&);
-
- If you are a Pascal programmer, you will recognize that the
- variable named in1 is treated just like a normal parameter in a
- Pascal call, a call by value. The variable named in2 however, is
- treated like a variable with the reserved word VAR used in front
- of it, usually referred to as a call by reference. As mentioned
- earlier, the reference variable used in C++ is actually a self
-
-
- Page 4-4
-
- Chapter 4 - Functions
-
- dereferencing pointer which refers to, or points to, the original
- value.
-
- When you compile and execute this program, you will find that the
- first variable got changed in the function but was returned to
- its original value when we returned to the main program. The
- second variable however, was changed in the function and the new
- value was reflected back into the variable in the main program
- which we can see when the values are listed on the monitor.
-
-
- DEFAULT PARAMETERS
- -----------------------------------------------------------------
- Examine the file named DEFAULT.CPP for an =================
- example of the use of default parameters in DEFAULT.CPP
- C++. This program really looks strange since =================
- it contains default values for some of the
- parameters in the prototype, but these default values are very
- useful as we will see shortly.
-
- This prototype says that the first parameter named length must be
- given for each call of this function because a default value is
- not supplied. The second parameter named width, however, is not
- required to be specified for each call, and if it is not
- specified, the value 2 will be used for the variable width within
- the function. Likewise, the third parameter is optional, and if
- it is not specified, the value of 3 will be used for height
- within the function.
-
- In line 11 of this program, all three parameters are specified so
- there is nothing unusual about this call from any other function
- call we have made. Only two values are specified in line 12
- however, so we will use the default value for the third parameter
- and the system acts as if we called it with get_value(x, y, 3)
- since the default value for the third value is 3. In line 13, we
- only specified one parameter which will be used for the first
- formal parameter, and the other two will be defaulted. The
- system will act as if we had called the function with
- get_volume(x, 2, 3). Note that the output from these three lines
- is reversed. This will be explained shortly.
-
- There are a few rules which should be obvious but will be stated
- anyway. Once a parameter is given a default value in the list of
- formal parameters, all of the remaining must have default values
- also. It is not possible to leave a hole in the middle of the
- list, only the trailing values can be defaulted. Of course, the
- defaulted values must be of the correct types or a compiler error
- will be issued. The default values can be given in either the
- prototype or the function header, but not in both. If they are
- given in both places, the compiler must not only use the default
- value, but it must carefully check to see that both values are
- identical. This could further complicate an already very
- complicated problem, that of writing a C++ compiler.
-
- Page 4-5
-
- Chapter 4 - Functions
-
- As a matter of style, it is highly recommended that the default
- values be given in the prototype rather than in the function.
- The reason will be obvious when we begin using object oriented
- programming techniques.
-
-
- WHY IS THE OUTPUT SCRAMBLED?
- -----------------------------------------------------------------
- When the compiler finds a cout statement, the complete line of
- code is initially scanned from right to left to evaluate any
- functions, then the data is output field by field from left to
- right. Therefore in line 11, get_value() is evaluated with its
- internal output displayed first. Then the fields of the cout are
- displayed from left to right with "Some box data is" displayed
- next. Finally, the result of the return from get_value() is
- output in int format, the type of the returned value. The end
- result is that the output is not in the expected order when lines
- 11 through 13 are executed. (The output is not what you would
- intuitively expect to happen so appears to be a deficiency in the
- language. A call to Borland International, the writers of Turbo
- C++ and Borland C++, verified that this is operating correctly.)
-
- We still have the problem of mixing cout and printf() output as
- discussed in chapter 1 while studying the program named
- MESSAGE.CPP. Eventually, all conforming compilers will overcome
- this problem.
-
- Lines 15 through 18 are similar to any two of the lines of code
- in lines 11 through 13, but are each separated into two lines so
- the output is in the expected order.
-
- Be sure to compile and execute DEFAULT.CPP after you understand
- it. Note that the funny output order will appear again later in
- this tutorial.
-
-
- VARIABLE NUMBER OF ARGUMENTS
- -----------------------------------------------------------------
- Examine the program named VARARGS.CPP for an =================
- illustration of the use of a variable number VARARGS.CPP
- of arguments in a function call. We have gone =================
- to a lot of trouble to get the compiler to
- help us by carefully checking how many parameters we use in the
- function calls and checking the types of the parameters. On rare
- occasion, we may wish to write a function that uses a variable
- number of parameters. The printf() function is a good example of
- this. ANSI-C has a series of three macros available in the
- "stdarg.h" header file to allow the use of a variable number of
- arguments. These are available for use with C++ also, but we
- need a way to eliminate the stronger type checking that is done
- with all C++ functions. The three dots illustrated in line 6
- will do this for us. This prototype says that a single argument
-
-
- Page 4-6
-
- Chapter 4 - Functions
-
- of type int is required as the first parameter, then no further
- type checking will be done by the compiler.
-
- You will note that the main program consists of three calls to
- the function, each with a different number of parameters, and the
- system does not balk at the differences in the function calls.
- In fact, you could put as many different types as you desire in
- the calls. As long as the first one is an int type variable, the
- system will do its best to compile and run it for you. Of course
- the compiler is ignoring all type checking beyond the first
- parameter so it is up to you to make sure you use the correct
- parameter types in this call.
-
- In this case, the first parameter gives the system the number of
- additional parameters to look for and handle. In this simple
- program, we simply display the numbers on the monitor to
- illustrate that they really did get handled properly.
-
- Of course, you realize that using a variable number of arguments
- in a function call can lead to very obscure code and should be
- used very little in a production program, but the capability
- exists if you need it. Be sure to compile and execute this
- program.
-
-
- FUNCTION NAME OVERLOADING
- -----------------------------------------------------------------
- Examine the file named OVERLOAD.CPP for an ==================
- example of a program with the function names OVERLOAD.CPP
- overloaded. This is not possible in ANSI-C, ==================
- but is perfectly legal and in fact used quite
- regularly in C++. At first this will seem a bit strange, but it
- is one of the keystones of object oriented programming. You will
- see its utility and purpose very clearly in later chapters of
- this tutorial.
-
- You will notice in this example program that there are three
- functions, in addition to the main function, and all three have
- the same name. Your first question is likely to be, "Which
- function do you call when you call do_stuff()?" That is a valid
- question and the answer is, the function that has the correct
- number of formal parameters of the correct types. If do_stuff()
- is called with an integer value or variable as its actual
- parameter, the function beginning in line 23 will be called and
- executed. If the single actual parameter is of type float, the
- function beginning in line 28 will be called, and if two floats
- are specified, the function beginning in line 33 will be called.
-
- It should be noted that the return type is not used to determine
- which function will be called. Only the types of the formal
- parameters are used to determine which overloaded function will
- be called.
-
-
- Page 4-7
-
- Chapter 4 - Functions
-
- The keyword overload used in line 4 tells the system that you
- really do intend to overload the name do_stuff, and the
- overloading is not merely an oversight. This is only required in
- C++ version 1.2. C++ version 2.0 and greater do not require the
- keyword overload but allows it to be used optionally in order to
- allow the existing body of C++ code to be compatible with newer
- compilers. It is not necessary to use this keyword because, when
- overloading is used in C++, it is generally used in a context in
- which it is obvious that the function name is overloaded.
-
- When the final C++ standard is completed, the use of this word
- may not be permitted. As was mentioned earlier, the C++ language
- is changing and we must be willing to change with it.
-
- The actual selection of which function to call is done at compile
- time, not at execution time, so the program is not slowed down.
- If each of the overloaded function names were changed to
- different names, each being unique, there would be no difference
- in execution size or speed of the resulting program.
-
- Overloading of function names may seem very strange to you, and
- it is strange if you are used to the rules of K&R or ANSI-C
- programming. As you gain experience with C++, you will feel
- very comfortable with this, and you will use it a lot in your C++
- programming.
-
- Note the use of the keyword const used in some of the function
- prototypes and headers. Once again, this prevents the programmer
- from accidentally changing the formal parameter within the
- function. In a function as short as these, there is no real
- problem with an accidental assignment. In a real function that
- you occasionally modify, you could easily forget the original
- intention of the use of a variable and attempt to change it
- during an extended debugging session.
-
- PROGRAMMING EXERCISES
- -----------------------------------------------------------------
- 1. Change the type of wings in the prototype of PROTYPE1.CPP to
- float so that it disagrees with the function definition to
- see if you get a compilation error.
-
- 2. Change the function definition in PROTYPE1.CPP to agree with
- the changed prototype. Compile and execute the program
- without changing the calls in lines 12 and 13. Explain the
- results.
-
- 3. In DEFAULT.CPP, remove the default value from the prototype
- for height only to see what kind of compiler error you get.
- Only the last values of the list can be defaulted.
-
- 4. In OVERLOAD.CPP, change the names of the three functions so
- that each is a unique name and compare the size of the
- resulting executable file with that given for the present
- program.
- Page 4-8
-