CIS Logo SVC Logo

   Computing & Information Systems
   Department

 

Schoology Facebook        Search CIS Site      Tutorials

Software Design Using C++



Complex C++ Functions



Introduction


This section shows how to use functions that have parameters and/or return values. All of this has to do with passing data into and out of functions. (For information on functions without parameters or return values, see the section on Simple Functions.)

Library Functions


The compiler has built-in library functions to accomplish a number of useful tasks. One such function is sqrt, which finds the square root of a number. This function takes one parameter and also returns a value. The parameter, given in parentheses after the function name, specifies the value whose square root we want to find. The return value will be that square root. Here is a typical way to call such a function:


float answer;
answer = sqrt(1.44);

As with any assignment statement we begin with the right hand side. The function call passes the 1.44 to the sqrt function, which hopefully comes up with 1.2 and passes that number back in the function name. This is the "return value", this 1.2. Here it is assigned into the variable answer. The return value of a function can also be printed out directly (as long as it is a printable item):


cout << "The square root is: " << sqrt(1.44) << endl;

In this case the 1.44 is again passed to the function, which computes and returns the value 1.2. Here the 1.2 is inserted right into the output statement, so that it is printed on the screen.

Next, look at root.cpp, a complete program that computes and prints a square root. Note that the cmath header must be included in order to use the sqrt function. The method of embedding the function call in an output statement was used. In its place we could have used an assignment statement to copy the return value into a variable and then could have have printed that variable.

Writing Your Own Functions


See the example area3.cpp. It shows another way to divide up our area of a rectangle problem. Some of the functions used have parameters and/or a return value.

The simplest function in this program is Explanation. Look at its function prototype:


void Explanation(void);

The void in parentheses means that there are no parameters. The void in front of the function name indicates that there is no return value. If you look at the actual code for the function, you will see that it just prints some messages.

Now consider the PrintArea function. Its prototype is shown below for you to examine. Once again, the void in front means that there is no value returned from the function. However, this function has a parameter called Area of type float. A parameter such as this is typically used to send a piece of data to the function. The "Given" section of the comments for this function make it clear: Area is being used to send in the area number that is to be printed. In general, the "Given" section describes those items that are being sent into a function (normally via parameters just like this one). Finally, note that the "Return" section in the comments states that this function returns nothing. All it does is to print something on the screen; nothing is sent back to the main function.


void PrintArea(float Area);

Let's look at the ComputeArea function next. Its function prototype has been copied in below. This one has 2 parameters, Length and Width. These are again used to send values into the function. This function also returns a value, as we can tell from the float in front of the function name. This specifies that the function returns a floating point value. A glance at the "Return" section of the comments tells us that this returned value is the desired area of our rectangle.


float ComputeArea(float Length, float Width);

Now look at the code for this function (inside a pair of braces, as usual). To actually return a value, the function must use a return statement (just like we use at the end of the main function). Here it is the only statement inside the function. In this case, the value of the expression Length * Width is found and then sent back via the function name.


return Length * Width;

Finally, look at the prototype for the GetValues function, copied in below for convenience. The void in front shows that no value is returned via the function name. However, there are 2 parameters, Length and Width. The parameter list is different from that used with the previous function, since an ampersand has been added in front of each parameter name (and before the type name float). The ampersand indicates a reference parameter. A reference parameter is used to send an answer back to whatever called this function. Thus, in our case we are sending two answers back: the length and the width.


void GetValues(float & Length, float & Width);

It should be obvious that sending an answer back in the function name is fine if there is just a single answer, but that the reference parameter mechanism is needed whenever you want to send back two or more answers. The "Return" section of the comments spell out that parameters Length and Width are being used to send back the length and width. Thus the "Return" section is used to describe values sent back by either the reference parameter method or the return in the function name mechanism. In the comments for a function, always identify each parameter by name when talking about it. This makes things much clearer to the reader, who after all may not be very familiar with your program.

The main function, reprinted below, is pretty simple. It declares some variables and then calls the various functions to carry out the work. First, Explanation is used to print out a description of the program. Then GetValues is used to ask the user for the length and width. This function returns these two answers into the variables Length and Width. In the main function, this is the first place where these two variables have sensible values. Prior to this they probably contained some garbage numbers.


int main(void)
   {
   float Length, Width, Area;

   Explanation();
   GetValues(Length, Width);
   Area = ComputeArea(Length, Width);
   PrintArea(Area);

   return 0;
   }

Let's continue examining the main function. Length and Width are next passed to the ComputeArea function, which finds the area and sends it back in the function name. The main function assigns that function name value into the variable Area. Finally, the value of Area is passed to the function PrintArea, which prints the area on the screen.

It is often helpful to draw pictures of our functions. The following drawing shows a picture of each of our 4 helping functions. Each function is pictured as a box with its name on it. The Explanation function is a solid box with no data flowing into it or out of it. The GetValues function shows the two values sent out of the function via the parameters. The PrintArea function shows the Area being sent into the function via the parameter. Finally, the ComputeArea function shows the Length and Width being passed into the function via the parameters and the answer, the area, being sent back out in the function name itself.

[drawing of the 4 functions]

A few notes are in order at this point. The parameters in a function call (such as those inside of main above) are called "actual parameters" while the parameters inside of a function prototype are called "formal parameters". The formal parameter names do not have to be the same as the actual parameter names, but the parameters do have to match up from left to right in terms of their type and their meaning. More advanced information about parameters can be found in the Intermediate section Functions and Parameters. Much of that information, however, is probably too advanced for those just learning to use functions.

There is clearly no need in our area problem to subdivide things into four functions. In fact, we previously solved the problem with only 2 helping functions and, in a really simple version, with only a main function. For guidance on how to subdivide a problem into functions, consider these principles. Each function should do one central, useful task. If the function only has one line of code in it, it is probably not too useful. If the function has 200 lines of code in it, it is probably too long and needs to farm out its work to helping functions or might have more than one central task, so that it should be divided up into several functions, each of which does just one overall task. Having just one main task for a function makes it much clearer for the human reader of the program to follow. The name of the function should be an indication of that main task. It is pretty common to have a GetData function to get some input from the user, to have a Print function to print some answers, and to have one or more Compute functions to calculate those answers.

Examples Using Dark GDK

Dark GDK's 2D primitives allow us to output filled rectangles but do not make provision for empty ones. In order to draw an empty rectangle, we would need to draw four perpendicular lines. Now, we could put this every place in our code where this is necessary. However, that would make for a lot of repeated code - and imagine if we wanted to make a change to the logic. We would have to change it in all of the places where we used it! Therefore, let us imagine that we have a function:


void drawEmptyBox(int ulx,int uly,int lrx, int lry) 
{
   // Draw line from upper left to upper right (NB: urx = lrx and ury = uly)
   dbLine(ulx,uly,lrx,uly);
   // Draw line from upper right to lower right
   dbLine(lrx,uly,lrx,lry);
   // Draw line from lower right to lower left (NB: llx = ulx and lly = lry)
   dbLine(lrx,lry,ulx,lry);
   // Draw line from lower left to upper left
   dbLine(ulx,lry,ulx,uly);
}

This function takes the same parameters as the Dark GDK function dbBox but instead draws four lines in the current ink color. Depending on the circumstances in our program, we may already have the upper-left coordinate point as well as the rectangle's width and height. However, it may be the case that we would not want to calculate the lower right coordinates in multiple places. Though this is simple to do, it would make the code at the place of invocation look rather sloppy. Therefore, we could write a function like the one below which does the calculation and calls our main drawing function:


void drawEmptyBoxWH(int ulx,int uly,int width,int height)
{
   drawEmptyBox(ulx,uly,ulx + width,uly + height);
}

Finally, let us consider the possibility that we may want to draw a square, already having the upper left coordinate point and the single side-length dimension. In such a case, we could write a function like the following:


void drawEmptySquare(int ulx,int uly,int sideLength)
{
   // NB: This could also call drawEmptyBoxWH(ulx,uly,sideLength,sideLength)
   //       However, that would require one more function call, thereby taking 
   //       more processing time
   drawEmptyBox(ulx,uly,ulx + sideLength,uly + sideLength);
}

Though these are rather fabricated situations, they serve to show some interesting aspects of how we will be using functions in the future. See DrawEmptyBox.cpp for a driver program with all of these functions in it. The output of the program should appear like this.

To continue with our drawing of a smiley, we can take a look at the program in SmileyBlink.cpp. This program draws a smiley to the screen, pauses, and then "blinks" the eye on the face by replacing the Ball-shaped eye with a horizontal line. The tasks of drawing the face and blinking its eye are split into different functions which are meticulously detailed in the file itself. For our purposes here, it is only important to draw your attention to the fact that the DarkGDK function (that is, the main driver function) is short and to the point because of the use of functions. Even though the function code is not being reused, it has made this program eminently readable.

Related Items

Back to the main page for Software Design Using C++

Author: Br. David Carlson with contributions by Br. Isidore Minerd
Last updated: August 27, 2009
Disclaimer