(cn)Chapter 6
(ct)Creating your own Functions
In this chapter, we’ll discuss how to make our programs more readable and efficient by creating our own functions. As we'll see during the course of the chapter, Functions are pieces of code that perform a single task, and promote a concept called modularity.
(1)Modular Programs Are Easier to Maintain and Understand
"Starting today," I said, as I began our sixth class, "and continuing for the next three weeks or so, we’ll be examining ways in which we can use some of C++’s object-oriented features to make programs that are more readable, more efficient, and easier to maintain. Even more importantly, you’ll discover object-oriented programming languages like C++ promote the concept of something called ‘software reuse,’ which means that a piece of code, once written, doesn’t have to be tossed away or rewritten for another program but can be incorporated into classes for use in another program. In other words, the same piece of code can be used in multiple programs. We’ll look at that in more detail next week when you see how you can create classes of your own. In today’s class, we’ll take the first step along the path of software reuse when you learn how to write functions of your own."
"But haven't we already written functions of our own?" Kate asked.
"To a degree that's true Kate," I said. "Every one of the programs we've written has contained a main() function."
"If I’m correct," Dave said, "I believe that every program that we’ve written in this class has had only one function—the main() function. Isn’t that right?"
"That’s right, Dave," I answered, "and you have probably noticed that as our programs have gotten more complex, the number of lines of code in the main() function has grown and grown. Today, you’ll learn how to create functions of your own that reside in the same file as the one in which the main() function is contained. Then, next week, you’ll learn how to create functions that reside in something known as a class, and which can then be executed or called from other programs."
"You said that the number of lines of code in the main() function has grown and grown," Kate said. "Is that bad? Is there a limit to the number of lines of code that can go into the main() function? Why do we need to write other functions?"
"There’s no limit to the number of lines of code that can be placed in the main() function, or in any function for that matter," I said. "However, the more lines of code contained in a function, the more difficult the program is to follow, understand, and maintain."
"What do you mean by maintain?" Rhonda asked. "Is that like car maintenance?"
"Maintaining a program," I answered, "means changing or modifying the program. Programs need to be maintained for a number of different reasons. Some programs need to be modified because of a change in the business environment for which the program is written. Other programs need to be modified due to new governmental regulations. Still other programs need to be modified because of requests from users. Regardless of the reason, you can be almost certain that any program you write will eventually need to be modified, if not by you, then by someone else. Even if all you need to change is a single line of code, if that line of code happens to appear in a main() function with hundreds or thousands of other lines of code, you’re going to have a heck of a time finding that line of code unless the program was written in a modular fashion."
"Modular?" Lou asked.
"You’ll see a little later on, Lou," I said, "that modular programs are programs that are written in distinct, logical units."
"Do programs need to be changed all that often that we need to worry about this?" Blaine asked.
"Most programs that are written for commercial purposes will at one time or other need to be changed." I answered. "In fact, it’s been estimated that the programming staff in a large corporation may spend up to 85 percent of its time modifying the code in already existing programs."
"That’s incredible," Chuck said. "So making programs easier to read and maintain is important."
"Absolutely," I said. "In fact, it’s pretty likely that the program we’re writing for Frank Olley will need to be changed at some point. If the English, Math, or Science Departments change the formula for the way a student’s final grade is calculated, we’ll need to change the Grade Calculation program."
"I see why programs need to be changed," Valerie said, "but how can creating functions of our own in addition to the main() function make that process easier?"
"So far," I answered, "in all of the code you’ve written for this class, I’ve pretty much told you exactly what line or lines of code to write and where to place them. In the real world, however, this won’t be the case. You’re more likely to be asked by a supervisor or project leader to make a functional change to a program. In other words, you’ll be told what change to make in terms like, ‘Change the federal tax withholding rate from 18 percent to 22 percent.’ It will be up to you to find the appropriate C++ file, locate the line or lines of code in that file that performs that calculation, decide upon the necessary changes, and then apply them. From experience, I can tell you if all of the code in your program is located within the main() function of a single C++ program file, finding and making changes to that code can be pretty tough."
"And this is where having more than one function will come in handy?" Rhonda asked. "I’m afraid I just don’t see why."
"I think I can help," Dave chimed in. "I work in a department that gets a tremendous amount of mail. We have a super-efficient secretary, Millie, who by the time I sit down in my cubicle each morning, has separated everyone’s mail and placed it on the individual’s desk. On those days when Millie isn’t in, the place is chaos. Anyone who is expecting an important piece of correspondence sifts through a huge pile of mail. Eventually they find the piece they’re looking for, but it’s a painstaking job, and sometimes they accidentally pull out a piece of mail belonging to someone else."
"So that huge pile of mail, Dave," Rhonda said, "is like having one big main() function?"
"That’s right," Dave replied. "Millie, by sorting and distributing the mail each morning, produces logical ‘modules’ of mail. It makes the whole process much easier."
"That’s a great analogy, Dave, thanks," I said. "In terms of modular programming, that means that when we write code, we should place code that performs a single task into a function of its own. For instance, if we write a program to calculate payroll, all of the code to calculate the federal withholding tax should be placed in a function of its own, ideally named in such a way that it conveys the meaning of what it does. Similarly, the code to calculate the state withholding tax should be placed in a function of its own. This process has traditionally been described as modular programming, although the concepts and techniques have been enhanced quite a bit by modern object-oriented programming languages such as C++."
(2)What Is a Function?
"So in theory," Bob said, "a function is code that performs a single task?"
"That’s right, Bob," I said. "For instance, last week, all of the code that we wrote for the Grade Calculation Project went into the main() function of the Grades.cpp file. By the end of today’s class, we’ll have taken that code and redistributed much of it into separate functions. For instance, all of the code that performs the calculation for the final grade of an English student will be placed in a function of its own called CalculateEnglishGrade(). In a similar way, the code to perform the calculation for the final grade of a math student will be placed in a function of its own called CalculateMathGrade(), and the code to perform the calculation for the final grade of a science student will be placed in a function of its own called CalculateScienceGrade()."
"I see what you’re getting at now," Ward said. "If the code to calculate the final grade for a math student is in a function of its own, I would think finding it and making the correct changes to it would be much easier."
"Right on the mark, Ward," I said. "Not only is code that is broken down and placed in functions easier to find and modify, but when it’s also bundled in the form of an object--something we'll do next week---it can be easily reused in other applications."
"How’s that?" Peter asked.
"Do you remember the toUpper() function we used last week?"," I asked. "It's a perfect example of code reusability. It’s probably no exaggeration to say that millions of C++ programs use the code in the toUpper() function to do exactly what we did last week--convert characters to upper case characters---yet the code in the toUpper() function was written just once."
I gave everyone a chance to take in what I was saying.
"Are there any rules or guidelines for writing functions?" Steve asked. "Do you write them from scratch right away, or do you place all of your code in the main() function and then at some point move the code out of there into separate functions as we're doing today?"
"With a little experience, Steve," I said, "you’ll find yourself writing functions of your own right from the very start of your program. It seems strange to you now because for the last five weeks we’ve dealt with just the single main() function, but the more programs you write, the more natural placing code in your own functions will become. Just remember: place code that performs a single task into a function of its own. Needless to say, we haven’t done that yet with the Grade Calculation Project, but that’s because we needed to concentrate on learning the fundamentals of the C++ language and how to create a working program before we could worry about making our program more readable, efficient, and easy to modify. For the remainder of today’s class we’ll worry about all of that, and in next week’s class and the one after that, we’ll learn how the functions we create today can be placed in C++ classes of their own that can then be incorporated into programs written by other programmers."
"Just like the toUpper() function?" Kate asked.
"You hit the nail on the head, Kate," I said. "Programmers who write good code and are insightful enough to place that code into classes are rewarded by having their code used by hundreds of other programmers—not only by programmers in their own companies but by programmers all over the world. We’ll learn more about how to create code and place it in those types of classes next week."
"I can imagine that’s quite an ego trip," Ward said, "having your code used like that—and it’s also quite an incentive for me to learn this language."
"I have just one question," Mary said. "If we take all of the code out of our main() function and place it in those other functions you mentioned, what will be left in the main() function?"
"We won’t take all of the code out of the main() function," I answered. "We know that only a C++ file containing a main() function can be executed from a command prompt, so the main() function can't disappear entirely. What will happen is that the main() function will 'shrink' so that it contains the code that ‘calls’ or requests the execution of the code contained in those other functions. The code in the main() function frequently resembles the outline or table of contents of a book, with each call to a function appearing as a chapter heading."
(2)Creating Your Own Functions
"This all sounds very exciting," Rhonda said. "So how do we create functions of our own, and what do we name them?"
"You can name your functions virtually anything you want, Rhonda," I said, "but be sure to pick a meaningful name. As far as how to create them, in today's class functions of your own will be contained in the same C++ file as the main() function. By convention, they should follow the main() function, and although it’s not required, it’s a good idea to separate any functions you write from the main() function with a blank line. Let’s examine a now-familiar C++ program containing just a single main() function and then modify it to include another function of our own."
I then displayed this code on the classroom projector:
//Example6_1.cpp
#include <iostream>
int main()
{
using namespace std;
cout << "I Love C++";
return 0;
}
"Look familiar?" I asked. "This is the first C++ program we wrote in the course. As you know, it displays the message ‘I love C++!’ in the C++ console window. Let’s see how we can take the code to display that message out of the main() function and place it in a function of its own, which we’ll call DisplayMessage()."
I then modified the code to look like this:
//Example6_2.cpp
#include <iostream>
void DisplayMessage(); // Function Prototype
int main()
{
using namespace std;
DisplayMessage(); // Call to Custom Function
return 0;
}
void DisplayMessage() // Custom Function
{
using namespace std;
cout << "I Love C++";
}
saved it as Example6_2.cpp, compiled it and executed it. The message "I love C++!" was displayed in the C++ console window.
"This program behaves in the same manner as the previous version did," I said, "but in this version of the program, the C++ instruction to display the message ‘I love C++!’ is no longer being executed directly from the main() function. Instead, the instruction is executed from a function called DisplayMessage(). C++ requires that we include something called a function prototype at the 'top' of our code. A function prototype tells C++ the name of the function that we'll be using, its return type, and the number and type of arguments--called its signature---that it accepts…"
(3)Function Prototype
"…notice how we included the function prototype for the DisplayMessage() function right after the include statement…"
void DisplayMessage(); // Function Prototype
"I was wondering what that was," Blaine said. "So that's not the function itself?"
"That's right Blaine," I said. "Each function contained in the file--other than the main() function--must have a function prototype specified. The function prototype doesn't contain any instructions--it just announces to C++ our intention to create a function with that name and 'signature'."
I waited a moment before continuing.
"…this is the code that tells C++ to execute the code in the DisplayMessage() function:"
DisplayMessage(); // Call to Custom Function
"Notice," I continued, "that the code to call or execute the DisplayMessage() function references the name of the function precisely because function names, like variable names, are case sensitive. Notice also that the DisplayMessage() function follows the main() function."
"Do we need to use the parentheses when we call the function?" Peter asked.
"I think we do," Kate said. "I accidentally coded this program without them, and although the program compiled OK, 'nothing' seemed to happen when I ran it."
"A good question Peter," I said, "and a good answer Kate--failing to include the parentheses in the function call can lead to all sorts of problems--although as you discovered, the program will compile cleanly. Let's take a closer look at the first line of the DisplayMessage() function now."
(3)Function Header
"I see that the first line of the DisplayMessage() function is different from the main() function," Kathy said. "I think at some point in the class you promised to explain that first line of the main() function in more detail—is now the time?"
"You’re right, Kathy," I said. "The first line of both functions, which is sometimes called the function header, the function definition, or the function signature, are different. And you’re also right that now is a great time to discuss what that first line means in some detail."
int main()
"That looks so complicated to me!" Rhonda said, looking at the main() function header on the classroom projector in a bewildered manner.
"Let’s see if this will help," I said, as I displayed this graphic on the classroom projector. "This is a schematic of the main() function header."
Illustration 1
"For comparison purposes," I said, "here’s the header for the DisplayMessage() function we just created:"
void DisplayMessage()
"It looks to me," Rhonda said, "that apart from the obvious difference in the names of the two functions, that main() has the word 'int' in front of it, and DisplayMessage() has the word 'void'. Is the main() function somehow special?"
"The only ‘special’ quality to the main() function," I said, "is that its code is automatically executed when we execute a C++ executable file from the command-line prompt. Refer to the chart on the classroom projector as we take a closer look at the declaration for the main() function. From left to right:"
"What’s a parameter?" Bob asked.
"A parameter is a piece of information," I said, "which in some way provides additional information to the function about how it should behave. A function that is defined with one or more parameters is seeking qualifying information from the code that calls it, and any code that executes that function is required to supply that qualifying information. Parameters are specified in the function header within parentheses. If the parentheses are empty, as is the case with the DisplayMessage() function, that means the function is defined without parameters, so the code calling the function need not worry about supplying it with qualifying information. By the way, you will also hear the term ‘arguments’ used interchangeably with parameters. Technically, functions are defined with parameters, and the qualifying information itself is passed to the header as an argument. There’s a subtle difference."
"Can we see an example of a function header with parameters?" Linda asked.
"Sure thing Linda," I answered. "Here's a preview of the DisplayMessage function header with a single String argument."
void DisplayMessage(string language)
"Is the word ‘language’ in the DisplayMessage() function header the name of the parameter?" Dave asked.
"That’s right, Dave," I said. "Parameters are named in the function header, and language is my choice for a meaningful parameter name. In actuality, the parameter name can be virtually anything."
"I know we’ve covered this before," Valerie said, "but does every program need to have a main() function?"
"I was about to ask that myself," Rhonda said. "Now that we have a function of our own—DisplayMessage() in Example6_2—is it really necessary to have a main() function also?"
"Yes it is, Rhonda." I said. "main() is a function that must be included in every C++ executable file that is to be executed from the command prompt. Only the executable file is required to have a main() function. Next week, we’ll create C++ classes which have no main() function, but they won’t be executed directly from the command prompt---instead, they'll be used as 'templates' from which we’ll create objects."
(3)The Return Type
"In the main() function definition, what is ‘void’?" Chuck asked. "That’s a strange-sounding word."
"In C++," I said, "functions perform some sort of processing, and many of them then return a value of some kind to the program that calls them. The return value can be used to inform the calling program as to the success or failure of the operation, or something along those lines. Ordinarily, the return type is a C++ data type, such as int or double. We'll also learn that the return type can also be an object. When the function, by design, returns no value, C++ needs to know this, and the return type of void is designated."
NOTE: The return type void indicates that the function does not return a value.
"So both the main() and DisplayMessage() functions perform some kind of processing---main() returns an Integer value, but DisplayMessage() returns no value to the code that calls them, is that right?" Dave asked.
"That’s exactly right, Dave," I said.
"I’m a bit confused," Rhonda said.
"Sometimes," I said, "I analogize functions to favors that you ask a friend to do for you. Perhaps you ask your friend to feed your fish while you’re away on vacation, and because you tend to be a worrier, you request a ‘return value’ in the form of a phone call or an e-mail from your friend to confirm that she actually fed the fish. On the other hand, your friend may be the type of person who never forgets to do anything, in which case, your mind is at ease, and no ‘return value’ is necessary."
"So sometimes," Kate said, "functions return a value, and sometimes they do not?"
"That’s right, Kate," I said, "and it’s entirely up to the designer of the function to decide. In the case of the DisplayMessage() function we wrote in Example6_2, did we return a value?"
Kate checked for a minute and then said, "No, the function header specifies void, and there's also no return statement within the body of the function."
"That’s right, we didn’t return a value from the DisplayMessage() function," I said, "but we could have. In the case of DisplayMessage(), I didn’t think it was really necessary."
"So the return type will be the same C++ data types we’ve already learned about?" Mary asked. "Like int, single, and double?"
"To name a few," I said. "The return data type can also be an object such as a String, or even an object of your own design, the kind we’ll create next week."
"I’m trying to recall if we’ve executed functions that return a value?" Lou asked.
"I think we have, Lou," Linda said. "When we executed the toUpper() function last week, it returned a value which we then assigned to a variable."
"That’s right, I forgot about that," Lou said.
"Can we see how to write a function of our own that returns a value?" Barbara asked.
"Sure thing, Barbara," I said. "We can modify the DisplayMessage() function we wrote in Example6_2 to return a value, in this case, an bool data type."
I then displayed this code on the classroom projector:
NOTE: Long lines of code in this book (such as the ones below to display output to the C++ console, have been broken up into several lines for formatting. You may type the code on a single line if you wish.
//Example6_3.cpp
#include <iostream>
bool DisplayMessage(); // Function Prototype
int main()
{
using namespace std;
bool messageDisplayed;
messageDisplayed = DisplayMessage();
cout << endl << "The value of messageDisplayed is "
<< boolalpha << messageDisplayed;
return 0;
}
bool DisplayMessage() // Custom Function
{
using namespace std;
cout << "I Love C++! ";
return true;
}
I then saved the program as Example6_3.cpp, and then compiled and executed it. The following screenshot was displayed on the classroom projector:
Illustration 2
"To return a value from a function," I said, "we need to do two things. First, we need to tell C++ that the function will return a Boolean value, which is either True or False. Because we need to include a function prototype in our program, we first change the return type of the function’s prototype from void to bool…"
bool DisplayMessage(); // Function Prototype
"…and then change the function header itself…"
bool DisplayMessage() {
"You said we need to do two things," Chuck said. "What’s the second?"
"Having told C++ that the function returns a value by declaring it as bool in both the function prototype and function header," I said, "we must now actually return the value from the DisplayMessage function. We do that by coding a return statement somewhere within the body of the function---usually the last statement. C++ is pretty smart about this: if we forget to code the return statement, the program simply won’t compile. Here’s the statement that returns the bool return value we committed to returning when we declared the function:"
return true;
CAUTION: Be sure to spell ‘true’ in lowercase letters in your C++ code. Spelling it ‘True’ will generate a compiler error.
"As you can see, all we need to do is execute the return statement, followed by an appropriate value for the declared data type of the return value. In this case, since we committed to returning a bool data type, we need to return the C++ value true or false."
"What does the program that calls the function do with the return value?" Kate asked.
"The code calling the DisplayMessage() function," I said, "can do one of three things with the return value. It can store the return value in a variable; it can use the return value in an expression, for instance, by redirecting it to cout object; or interestingly enough, it can choose to ignore the return value simply by executing the function and not doing anything at all with the return value. In this example code, we declared a bool variable called messageDisplayed in which we stored the return value from the function call. Remember, since we declared the DisplayMessage() function with a return type of bool, the variable we declare to store the return value should also be declared as a bool data type also:"
bool messageDisplayed;
"Having declared the messageDisplayed variable, we now execute the DisplayMessage() function, but notice how we place the call to the function to the right of the assignment operator (=). In this way, after the DisplayMessage() function executes, its return value is immediately stored in the variable messageDisplayed:"
messageDisplayed = DisplayMessage();
"Finally, we display the return value stored in the messageDisplayed variable to the C++ console…
cout << endl << "The value of messageDisplayed is "
<< boolalpha << messageDisplayed;
"I should mention here," I said, "that instead of storing the return value of DisplayMessage() in a variable the way we did here, we could have used the return value directly like this::"
//Example6_4.cpp
#include <iostream>
bool DisplayMessage(); // Function Prototype
int main()
{
using namespace std;
cout << endl << "The value of messageDisplayed is "
<< boolalpha << DisplayMessage();
return 0;
}
bool DisplayMessage() // Custom Function
{
using namespace std;
cout << "I Love C++! ";
return true;
}
"This version of the code is a bit trickier to follow, but many C++ programmers love the compact nature of this style of code—just something to watch out for."
"I’m surprised," Rhonda said. "I actually understand what’s going on here."
"Can a function return more than one value?" Chuck asked.
"Excellent question, Chuck," I replied. "The answer is no. A function is limited to returning just a single return value. However, it is possible to return an array. An array is a data structure that is actually a collection of variables. So there is a way around the limitation of returning just a single return value."
(3)Function Parameters and Arguments
We were making some pretty good progress in examining the function header. "What’s next in the function header?" I asked.
"After the word int in the main() function," Steve said, "comes the name of the function—no problem there. But then comes the part that confuses me: the parentheses."
I displayed the main() function header on the classroom projector once again:
int main()
"Let me assure you, Steve," I said, "the parentheses confuse everyone at first--I still remember the first time I saw them--couldn't understand what they were there for. Just remember, the parentheses are used to designate the parameter list--and if the parentheses are empty, that means that the function will be accepting no parameters. As is the case with the main() function, with the DisplayMessage() function, there are no parameters defined either, and that’s why there’s just an empty set of parentheses:"
bool DisplayMessage() // Custom Function
"What are parameters, anyway?" Linda asked. "Have we executed any functions containing them?"
"I think we have," Dave chimed in. "Aren't parameters the same as arguments, like the one we've passed to some of the functions we’ve executed like atoi() and toUpper()?"
"You’re right, Dave," I said. "Arguments are the actual values that we pass to a function. Many programmers use the terms arguments and parameters interchangeably, and really, only a computer scientist would argue with you. In theory, parameters are the names that appear in a function header, and arguments are the actual values that are passed to the function by the code that calls it. For each parameter in the function’s header, there must be a corresponding argument passed to it."
"So parameters appear in the function header, and arguments are the actual values passed to the function when it is called?" Dave asked.
"Perfect, Dave," I said. "I—"
"I know," Rhonda said, laughing. "You couldn’t have said it any better yourself!"
"Can we modify the DisplayMessage() function to include a parameter?" Linda asked.
"I don’t see why not," I answered. "Let’s do this: let’s modify the DisplayMessage() function to allow the programmer to pass an argument specifying his favorite programming language." I thought for a moment and then displayed this code on the classroom projector:
//Example6_5.cpp
#include <iostream>
#include <string>
using namespace std;
void DisplayMessage(string language); // Function Prototype
int main()
{
DisplayMessage("Java");
DisplayMessage("Visual Basic");
DisplayMessage("C++");
return 0;
}
void DisplayMessage(string language) // Custom Function
{
cout << "I Love " << language << endl;
}
I saved the program as Example6_5.cpp, compiled and then executed it. The following screenshot was displayed on the classroom projector:
Illustration 3
"Let’s take a look at the new header for the DisplayMessage() function," I said. "I decided in this version of the program to go back to a void return type--returning a value from this function doesn’t really add anything to the program. Here’s the original function header:"
void DisplayMessage() // Custom Function
"And here’s the new function header declared with a parameter:"
void DisplayMessage(string language) // Custom Function
"Within the parentheses," I continued, "we are telling C++ that the DisplayMessage() function will accept a single parameter called language and that the parameter will be a String data type."
I could see some confusion in the eyes of my students, but I knew that would be cleared up momentarily.
"Let’s see the code to call the DisplayMessage() function." I said. "Calling a function that requires a parameter is easy, provided you know the function’s signature."
"Signature?" Joe asked.
"The function’s signature is the number and type of arguments required," I said. "Here we know that we need to pass DisplayMessage() just a single String argument, and we do that—actually executing it three times—with this code:"
DisplayMessage("Java");
DisplayMessage("Visual Basic");
DisplayMessage("C++");
"…and because the function requires a String argument, we enclose the argument within quotation marks--which is require for a String literal."
"I’m a little confused as to what the DisplayMessage() function does with the argument once it receives it from the calling code," Barbara said. "Can you clear that up?"
"I’ll try, Barbara," I answered. "Let’s look at the code from the body of the Example6_4 program, in which the program was hard coded to display C++ as its favorite language:"
cout << "I Love C++! ";
"Here’s the modified code, which uses the parameter language in conjunction with the cout object to write to the display of the C++ Console:"
cout << "I Love " << language << endl;
"Is language a variable?" Lou asked. "And if so, why isn’t it declared within the body of the function?"
"Parameters are a lot like variables," I said, "but they don’t need to be declared within the body of the function since they are declared within the function header."
"That makes sense," Barbara said.
"I just noticed something," Dave said. "You took the 'using namespace' statement out of the main() function and put it right after the include statements. Why is that?"
"Good observation Dave," I replied. "It's because of our use of the string type parameter in the function header for DisplayMessage. C++ needs the help of the std namespace in order to work with the string data type, and because the function prototype and function header appears 'outside' of the main() function we need to include the 'using namespace' statement outside of the main() function as well. One side benefit of this is that we no longer need to include the using namespace statement inside of the main() function, or within the DisplayMessage() function either. Technically, we've given the std namespace global scope. The alternative is to write the function prototype and the function header like this…"
void DisplayMessage(std::string language)
NOTE: Some C++ programmers prefer not to use Global namespace references like this, and would opt for the longer Function prototype and header instead. In the interest of saving space, for the remainder of the book we'll code a Global namespace reference.
"I have another question," Barbara said. "Is it possible to create a function that accepts more than one argument, and if so, how does C++ know which parameter is which when the code that calls the function passes the arguments?"
"Another good question," I said. "Yes, it is common to design a function that accepts more than one argument. In C++, arguments are passed positionally. That means that if the function’s header specifies two parameters, C++ assumes that the first argument passed to the function is the first parameter, the second argument passed to the function is the second parameter, and so on. Let me show you exactly what I mean by modifying the code we just wrote to accept two parameters."
I then modified the code from Example6_5 to look like this, saved it as Example6_6.cpp, and displayed it on the classroom projector:
//Example6_6.cpp
#include <iostream>
#include <string>
using namespace std;
void DisplayMessage(string language, string howMuch);
int main()
{
DisplayMessage("Java", "a bunch");
DisplayMessage("Visual Basic", "lots");
DisplayMessage("C++", "a lot more");
return 0;
}
void DisplayMessage(string language, string howMuch)
{
cout << "I Love " << language << " " << howMuch << endl;
}
"Notice the difference in both the function prototype and the function header for DisplayMessage()," I said. "They are both now defined with two String parameters: language and howMuch. Both of these parameters are used in the body of the function along with the cout object:"
void DisplayMessage(string language, string howMuch)
{
cout << "I Love " << language << " " << howMuch << endl;
}
"Also, as you would expect, if the function header now specifies two String parameters, the call to the function must specify two String arguments, which appear after the function name within parentheses, separated by a comma. Notice that because these are String arguments, they are enclosed within quotation marks:"
DisplayMessage("Java", "a bunch");
DisplayMessage("Visual Basic", "lots");
DisplayMessage("C++", "a lot more");
I then compiled and executed the modified program, and the following screenshot appeared on the classroom projector:
Illustration 4
"Now the calling program can not only designate a favorite language, but also an assessment as to how much the user likes it," I said.
"I can see," Ward said, "that two parameters make this function even more flexible. This stuff is pretty neat."
"You’re right, Ward," I said. "The more parameters a function accepts, the more flexible it can be. Of course, the more parameters a function accepts, the more complex the code in the function needs to be to handle the multiple arguments that it will receive. Later on today, we’ll create functions for the Grade Calculation Project that will accept several parameters and you’ll see what I mean."
"Suppose we had forgotten to supply the function call with two arguments?" Lou asked. "What would have happened? Would the program bomb when we ran it?"
"That depends," I said, "on a number of things. When we compile a C++ program, the C++ compiler will always check the function call against the function header---and if there's something wrong with the number and type of arguments being used in the function call, the compiler simply won’t compile an executable file. However, sometimes programs are executed, referencing a function outside of the program itself that has been changed since the program was compiled---in that case, the program can bomb at runtime."
"How is it possible to call a function outside of the actual program?" Kate asked.
"We've been doing this during the entire course." I said, "For instance, the toUpper function we coded last week in our Grades program wasn't included in the Grades.cpp file--it's a function that is included in the iostream library we reference via the include statement. If the toUpper function's number and type of arguments were changed after we compiled a program using it, the program would bomb at run time. That’s why when you change a function definition the way we just did here, you have to be very careful, especially in a corporate or commercial environment, when many other programs can be using your function."
"What do you mean?" Valerie asked.
"If you change the function’s signature," I said, "which is the number, type, or even order of the parameters, programs that have already been written to call your function may bomb with runtime errors. Even worse, they could execute with incorrect results."
"Do changes to a function’s signature happen a lot in the real world?" Blaine asked.
"Functions do change in the real world," I said, "and it’s vitally important not to change a function’s signature when they do. When you do that, in programming talk, you have ‘broken’ the client’s code. However, sometimes changing a function like this just can’t be helped. A function that you designed and coded last year may, in some cases, require more information from the calling program in order to do its job."
"How can you implement a change like that without changing the function’s signature?" Bob asked.
"Frequently you can’t," I said. "But fortunately, in C++, it’s possible to have more than one function with the same name but with different signatures. This is called ‘function overloading,’ and it enables existing programs that call the function with its old signature to run fine and at the same time permits new programs to be written calling the function with its new signature. But let’s put off the topic of Function Overloading for just a few minutes longer. Right now, I’d like to discuss an alternative way to passing arguments to a function."
"What do you mean?" Rhonda asked.
"So far," I replied, "we’ve passed String literals to the DisplayMessage() function we’ve designed."
"What else can we pass?" Chuck asked.
"We can pass a variable," I said. "Let me show you."
I then displayed this code on the classroom projector:
//Example6_7.cpp
#include <iostream>
#include <string>
using namespace std;
void DisplayMessage(string language, string howMuch);
int main()
{
string favorite = "C++";
string intensity = "enormously";
DisplayMessage(favorite, intensity);
return 0;
}
void DisplayMessage(string language, string howMuch)
{
cout << "I Love " << language << " " << howMuch << endl;
}
I then saved the program as Example6_7.cpp, compiled and executed it---the following screenshot was displayed on the classroom projector:
Illustration 5
"This version of the program," I said, "displays just a single message in the C++ console window, but aside from that, behaves in the same manner as the other version did. However, we’ve changed the code by first declaring two String variables called favorite and intensity and assigning them values:"
string favorite = "C++";
string intensity = "enormously";
"And then we passed them as arguments to the DisplayMessage() function:"
DisplayMessage(favorite, intensity);
"Doing this has no impact on the execution of the function—the function doesn’t really care whether a String literal is passed to it as an argument or a variable is passed."
(3)In C++, by Default, Arguments Are Passed by Value
"Suppose," Linda said, "that for some reason, within the body of the function, we change the value of the passed argument. Does that have any effect on the value of the variable in the code that called it?"
"I’m not sure I know what Linda is asking," Rhonda said.
"Let me try to explain, Rhonda" I said. "In some programming languages, when a change is made to the value of a parameter that is passed to a procedure or function via a variable, as we did here, the value of the variable itself back in the code that called it is also changed."
I gave everyone a moment to think about that.
"Is that good?" Joe asked.
"Some programmers find this a convenient way of arriving at a programming solution," I said. "In most programming languages, variables can be passed as arguments to a procedure or function either by value or by reference. ‘By value’ simply means that the actual value of the variable is passed to the function as an argument, and in that case, changing the parameter within the body of the function has no impact on the variable in the calling code. When a variable is passed as an argument to a function ‘by reference,’ it isn’t the actual value of the variable that is passed to the function, but the actual address of the variable in the computer’s memory. That means that when the function changes the value of the parameter, it directly updates the value of the variable back in the calling code."
"What happened here?" Mary said.
"In C++," I said, "by default, variables are passed by value only. That means that although a variable is passed as an argument to a function, unless we want to, there’s no way that changing the value of the parameter within the body of the function can impact the value of the variable in the code that calls the function."
"Can we see an example of both?" Mary asked.
"Sure thing, Mary," I answered. "Let’s start by passing a variable as an argument by value."
I then displayed this code on the classroom projector:
//Example6_8.cpp
#include <iostream>
#include <string>
using namespace std;
void DisplayMessage(string language, string howMuch);
int main()
{
string favorite = "C++";
string intensity = "enormously";
DisplayMessage(favorite, intensity);
cout << "The value of favorite in main() is " << favorite << endl;
return 0;
}
void DisplayMessage(string favorite, string intensity)
{
cout << "The value of favorite in DisplayMessage() is "
<< favorite << endl;
favorite = "VB";
cout << "The value of favorite in DisplayMessage() is now "
<< favorite << endl;
}
I saved the program as Example6_8.cpp, and compiled and executed it---and the following screenshot was displayed on the classroom projector:
Illustration 6
"Let me explain what’s going on here," I said. "As we did in Example6_7, within the main() function, we declared two variables called favorite and intensity and initialized both of them with values:"
string favorite = "C++";
string intensity = "enormously";
"We then passed the variables as arguments to the DisplayMessage() function:"
DisplayMessage(favorite, intensity);
"We didn’t need to do this to prove that by default C++ passes variables by value," I said, "but notice that we’ve changed the names of the parameters within the function’s header. We’re still accepting two String parameters, but in this version of the program we’ve named them with the same names as the variables in the main() function:"
void DisplayMessage(string favorite, string intensity)
"Can we do that?" Ward asked. "Shouldn’t the names of the parameters be different from the variables in the main() function?"
"They don’t have to be," I said, "I know that in Example6_6and Example6_7, the parameter names were different from the names of the variables in the main() function, but there’s no rule that they have to be. The variables declared in the main() function are local to the main() function, and the parameters in the DisplayMessage() function are local to the function—C++ considers each of these variables to be different animals. We prove that by executing this line of code within the DisplayMessage() function that displays, in the C++ console, the original value of the argument passed to it as the favorite parameter, which is C++:"
cout << "The value of favorite in DisplayMessage() is "
<< favorite << endl;
"With this line of code, we change the value of the favorite parameter to ‘VB’:"
favorite = "VB";
"You might be inclined to believe that we have also changed the value of the favorite variable in the main() function, but you’ll see in a moment we haven’t. First, we prove that the value of the favorite parameter has indeed been changed by executing this line of code:"
cout << "The value of favorite in DisplayMessage() is now " << favorite << endl;
"This displays, in the C++ console, the altered value of the favorite parameter, which is now ‘VB’. The DisplayMessage() function ends, and this line of code is then executed from the body of the main() function, proving that the value of the favorite variable in the main() function has not changed."
cout << "The value of favorite in main() is " << favorite << endl;
"We’ve proven," I concluded, "that changing the value of the favorite parameter within the DisplayMessage() function has had no impact on the value of the favorite variable in the main() function."
"I think I understand what’s going on here," Linda said. "Even though the variables in the main() function and the parameters in the DisplayMessage() function have the same names, they’re really separate things, aren’t they?"
"That’s right, Linda," I said. "Both the variables and the parameters are declared ‘local’ to each function in which they appear."
(3)In C++, to Pass Arguments by Reference, Use the ref Keyword
"Now what about that other way of passing arguments?" Kate said. "You said that it’s possible to pass a variable as an argument to a function and have the function change the value of the variable back in the code that called it?"
"That’s right, Kate," I said. "Take a look at this code, which is basically the same as the code from Example6_8, except this time we are passing variables as arguments to DisplayMessage() by reference, not by value:"
//Example6_9.cpp
#include <iostream>
#include <string>
using namespace std;
void DisplayMessage(string &language, string &howMuch);
int main()
{
string favorite = "C++";
string intensity = "enormously";
DisplayMessage(favorite, intensity);
cout << "The value of favorite in main() is " << favorite << endl;
return 0;
}
void DisplayMessage(string &favorite, string &intensity)
{
cout << "The value of favorite in DisplayMessage() is "
<< favorite << endl;
favorite = "VB";
cout << "The value of favorite in DisplayMessage() is now "
<< favorite << endl;
}
In order to pass a variable by reference, we need to preface the name of the variable in both the function prototype and the function header with an ampersand:
void DisplayMessage(string &language, string &howMuch);
"Let’s see the difference," I said, as I saved the program as Example6_9.cpp, compiled and then executed it:
Illustration 7
"I see what happened," Linda said excitedly. "This time, the DisplayMessage() function changed the value of the variables favorite and intensity in the main() function."
NOTE: A little later on in the book, we'll learn that it's also possible to get the same results by working with C++ pointers.
"That’s right, Linda," I said, "and that’s because this time, by specifying an ampersand along with the parameter names in the function header and prototype, the memory address of the favorite and intensity variables were passed to the DisplayMessage() function. When the DisplayMessage() function changed the value of the favorite parameter, C++ was given direct access to the value of the favorite variable back in the main() function:"
favorite = "VB";
"I think I understand what’s going on here," Rhonda said. "Now, did you say that there are certain types of problems that can be more easily solved by passing a variable by reference?"
"That’s true, Rhonda," I said, "but you needn’t concern yourself with those in this course. While you’re learning C++, don’t worry about passing arguments by reference—the default by value will be just fine."
(4)Variable Scope
"You’ve used the term ‘local’ several times this morning," Blaine said. "Can you tell us exactly what it means?"
"Local is a term that refers to the scope of a variable," I said. "A variable’s scope describes what other parts of your program can ‘see’ the variable. In C++, there are three types of variables: Instance, Class, and Local variables. We’ll discuss both Instance variables and Class variables next week. Local variables are variables declared within a function. A variable declared within a function can be seen or accessed only by code within that same function."
"So a local variable is one declared within a function?" Kate asked.
"That’s basically correct, Kate," I said. "Technically, a local variable is one declared within a block, that is, within a pair of braces. That means that if you declare a variable within the braces of an If statement, the variable can only be seen by the code within the If statement."
"I think at work I’ve seen some variables declared just above the main() function," Linda said. "They don’t appear to belong to any function."
"Those are Global, Static or Class variables," I said. "We examined a Global variable a few weeks ago in Example3_8. We'll talk more about Static and Class Variables next week."
(4)Variable Lifetime
"I’ve heard some programmers at work refer to the lifetime of a variable," Valerie said. "Is lifetime the same as scope?"
"No, but they are related," I replied. "Scope affects what parts of your program can see the variable. Lifetime, on the other hand, affects how long your variable lives. A variable declared as a local variable within a function has local scope and can only be seen by other code within the function. It’s also ‘born’ when its declaration statement within the function is executed and ‘dies’ after the last line of code in the function is executed. Next week, we’ll see that the lifetime for Global, Static and Class variables are different. In the case of a Global variable, it exists for as long as the program is running. In the case of a Class variable, it exists as long as an object that is created from its class exists. Static variables may live even longer: as long as any object created from its class exists. More on that next week."
We had been working for quite some time, so I suggested we all take a break before completing our first hands-on exercises of the day.
(2)Using Functions to Fine-Tune Your Code
Fifteen minutes later, when we returned from break, I resumed class by reminding my students that the main benefit to creating functions of our own—custom functions, as I call them—is that it promotes program modularity.
"Remember," I said, "modularity means that, as much as possible, you should create functions in your programs that perform one function and one function only. In the long run, this makes your programs easier to read, understand, and modify in the future. Creating custom functions and placing them in Instantiable classes allows our code to be easily used by other programmers. That’s something we’ll do next week. Today, I have a pretty extensive first exercise for you to complete. There’s a lot of code to it, and as you write it, you’ll find that all of it is being placed within the main() function of the class. As you complete the exercise, try to think of ways that you could use custom functions to make the program modular, because that’s exactly what we’ll be doing in the next exercise."
I then distributed the exercise for the class to complete.
Begin Exercise
(7)Exercise 6-1: The Smiley National Bank Program with All of the Code in the main() Function
In this exercise, you’ll write a program that allows the user of the program to display their bank balance or make deposits and withdrawals from their account.
//Practice6_1.cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
float balance = 0;
float newBalance = 0;
float adjustment = 0;
char response[256];
string moreBankingBusiness;
cout << "Do you want to do some banking? ";
cin >> moreBankingBusiness;
for (int i = 0;
i < moreBankingBusiness.length(); i++) {
moreBankingBusiness[i] =
toupper (moreBankingBusiness[i]);
}
while (moreBankingBusiness == "YES") {
//What type of business are we doing?
cout << "What would you like to do? " <<
"(1=Deposit, 2=Withdraw, 3=Get Balance): ";
cin.getline(response,256);
if (strlen(response) == 0) {
cout << "You must make a selection";
return 1;
}
else
if (atoi(response) < 1 |
atoi(response) > 3) {
cout << response <<
" - is not a valid banking function";
return 1;
}
//1 is a Deposit
if (atoi(response) == 1) {
cout << "Enter the Deposit Amount: ";
cin >> adjustment;
newBalance = balance + adjustment;
cout << endl << endl <<
"*** SMILEY NATIONAL BANK ***" <<
endl << endl;
cout << "Old Balance is: " << balance << endl;
cout << "Adjustment is: +" << adjustment << endl;
cout << "New Balance is: " << newBalance <<
endl <<endl;
}
//2 is a Withdrawal
if (atoi(response) == 2) {
cout << "Enter the Withdrawal Amount: ";
cin >> adjustment;
newBalance = balance - adjustment;
cout << endl << endl <<
"*** SMILEY NATIONAL BANK ***" <<
endl << endl;
cout << "Old Balance is: " << balance << endl;
cout << "Adjustment is: -" << adjustment << endl;
cout << "New Balance is: " << newBalance <<
endl <<endl;
}
// 3 is a Balance Inquiry
if (atoi(response) == 3) {
cout << endl << endl <<
"*** SMILEY NATIONAL BANK ***" <<
endl << endl;
cout << "Your current Balance is: " <<
newBalance << endl <<endl;
}
balance = newBalance;
cout << "Do you have more banking business? ";
cin >> moreBankingBusiness;
for (int i = 0;
i < moreBankingBusiness.length(); i++) {
moreBankingBusiness[i] =
toupper (moreBankingBusiness[i]);
}
} // end of while
cout << endl < endl << "Thanks for banking with us!";
return 0;
}
Illustration 8
Illustration 9
Illustration 10
Illustration 11
End Exercise
Begin Discussion
(7)Discussion
Although this program was very tedious to type, everyone was pretty comfortable with the code in it. There really wasn’t anything new in the program, and in about 15 minutes, all of my students had successfully coded the exercise.
"This program," I said, "is one we’ll be working with in quite a few exercises and is a good example of the type of program you may be asked to develop in the future. It’s also a good example of the type of program that can be enhanced significantly by writing custom functions. As you undoubtedly noticed while you keyed in the program, there’s a lot of code, and all of it is in the main() function. As I mentioned earlier, having all of your code in the main() function makes reading and understanding the code difficult, and it toughens the task of making modifications to this program if they are ever required."
"I agree with that," Kate said. "I made a mistake or two while coding it, and trying to find it was certainly compounded by the length of the code and the fact that it’s all in one place. Will we be able to break the main() function into other functions?"
"I think so, Kate," I said. "If we examine the code in the main() function, we’ll find that we’re performing three distinct tasks: making a deposit, making a withdrawal, and displaying a balance. Those three tasks, according to what you’ve learned today about program modularity, should be in three separate custom functions, and that’s what we’ll be doing in the next exercise. Before we start with that, do you have any questions about anything in this code?"
"I think just about everything in this program is something we’ve done before," Steve said, "but I was a little confused about the two variables, balance and newBalance. Was the reason we needed to have these two variables because we chose to display both the old balance and the new balance whenever the user made a transaction?"
"That’s exactly the case, Steve," I answered. "It isn’t until after we display a confirmation of the user’s transaction that we actually calculate a new balance. For that reason, we need to keep both the old balance and the new balance in memory to display both in the C++ console. The old balance is no problem—it’s stored in the balance variable. To calculate the new balance, we execute one of two lines of code, depending upon the transaction type. If a deposit was made, we perform this calculation:"
newBalance = balance + adjustment;
"And if the user makes a withdrawal we perform this calculation:"
newBalance = balance - adjustment;
"Regardless of the type of transaction, though, just before asking the user if she has more banking business, we must set the value of the balance variable equal to the value of the newBalance variable:"
cout << "Do you have more banking business? ";
cin >> moreBankingBusiness;
"Makes sense to me," Steve answered. "Thanks for the explanation."
I waited to see if there were more questions before continuing.
"OK," I said, "let’s take a shot at modifying this program to use three custom functions: one for a deposit, one for a withdrawal, and one to display a balance. There will still be some code in the main() function—we’re not moving it all into custom functions—but the main() function will appear more condensed this time around when we add the calls to the three other functions. When all is said and done, the new version of the program should behave identically to the one we just wrote, but you should find the program easier to read and much easier to modify if necessary. In fact, we’ll be doing just that next week when we move the code in the three functions you are about to create and place them into Instantiable classes."
End Discussion
No one had any questions about what we were about to do, so I distributed this exercise for my class to complete.
Begin Exercise
(7)Exercise 6-2: The Smiley National Bank Program with Three Custom Functions
In this exercise, you’ll modify the program you wrote in Exercise 6-1, taking much of the code in the main() function and placing it in one of three custom functions you’ll create.
//Practice6_2.cpp
#include <iostream>
#include <string>
using namespace std;
void MakeDeposit();
void MakeWithdrawal();
void GetBalance();
float balance = 0;
float newBalance = 0;
float adjustment = 0;
int main()
{
char response[256];
string moreBankingBusiness;
cout << "Do you want to do some banking? ";
cin >> moreBankingBusiness;
for (int i = 0; i < moreBankingBusiness.length(); i++) {
moreBankingBusiness[i] =
toupper (moreBankingBusiness[i]);
}
while (moreBankingBusiness == "YES") {
cout << "What would you like to do? " <<
"(1=Deposit, 2=Withdraw, 3=Get Balance): ";
cin.getline(response,256);
if (strlen(response) == 0) {
cout << "You must make a selection";
return 1;
}
else
if (atoi(response) < 1 |
atoi(response) > 3) {
cout << response <<
" - is not a valid banking function";
return 1;
}
if (atoi(response) == 1) {
MakeDeposit();
}
if (atoi(response) == 2) {
MakeWithdrawal();
}
if (atoi(response) == 3) {
GetBalance();
}
balance = newBalance;
cout << "Do you have more banking business? ";
cin >> moreBankingBusiness;
for (int i = 0;
i < moreBankingBusiness.length(); i++) {
moreBankingBusiness[i] =
toupper (moreBankingBusiness[i]);
}
} // end of while
cout << endl << endl << "Thanks for banking with us!";
return 0;
}
void MakeDeposit()
{
cout << "Enter the Deposit Amount: ";
cin >> adjustment;
newBalance = balance + adjustment;
cout << endl << endl <<
"*** SMILEY NATIONAL BANK ***" << endl << endl;
cout << "Old Balance is: " << balance << endl;
cout << "Adjustment is: +" << adjustment << endl;
cout << "New Balance is: " << newBalance
<< endl <<endl;
}
void MakeWithdrawal()
{
cout << "Enter the Withdrawal Amount: ";
cin >> adjustment;
newBalance = balance - adjustment;
cout << endl << endl <<
"*** SMILEY NATIONAL BANK ***" << endl << endl;
cout << "Old Balance is: " << balance << endl;
cout << "Adjustment is: -" << adjustment << endl;
cout << "New Balance is: " << newBalance
<< endl <<endl;
}
void GetBalance()
{
cout << endl << endl <<
"*** SMILEY NATIONAL BANK ***" << endl << endl;
cout << "Your current Balance is: " <<
newBalance << endl <<endl;
}
End Exercise
Begin Discussion
(7)Discussion
During the completion of this exercise, the question as to the most efficient way to modify the code from the previous exercise came up. Many of my students created a new C++ file and copied and pasted the old code into the new file. Then they modified the old code for the new exercise. Copying and pasting is not without its perils, however, and doing so in order to create the Practice6_2.cpp file was probably no quicker than just creating the code from scratch.
"This program should behave the same as the previous version, is that right?" Rhonda asked.
"That’s right, Rhonda," I said, but I could tell from her face that something wasn’t right. I paid a quick visit to her PC and discovered that even though she had properly created the three custom functions, she had failed to call them from the main() function of the class. Therefore, the program wasn’t permitting her to do any banking business.
"The behavior of the program hasn’t changed," I said, after getting Rhonda on the right track. "What we’ve done is take the code to make a deposit, a withdrawal, and display a balance out of the main() function and move it into three custom functions called MakeDeposit(), MakeWithdrawal(), and GetBalance()."
"In addition to those obvious changes," Dave said, "I did notice a few other subtle modifications in the program. Can you go over those?"
"Sure thing, Dave," I said. "I suspect you’re talking about the three variables: balance, newBalance, and adjustment, that are declared outside of any of the four functions that we now have in the class."
"Why are they there?" Blaine asked. "Are these the Global variables you were talking about?"
"That's excellent Blaine," I said. "As I mentioned before our break, variables declared within a function have local scope. That means that a variable that is declared within the MakeDeposit() function cannot be seen by code outside of it. Also, when the MakeDeposit() function ends, the variable and its value dies. All three of these variables have values that need to be seen by code in the three custom functions. The only way to give the code in all three of those functions access to the value in a variable is to declare the variable as a Global variable, which is what happens when the variable is declared outside of a function:"
static float balance = 0;
static float newBalance = 0;
static float adjustment = 0;
"Global variables," I said, "are accessible by every function in the program, and their values live for as long as our program is running. Aside from these changes and the creation of our three custom functions, I believe the program is now pretty much identical to the previous version."
End Discussion
No one seemed to disagree, so I continued.
(2)Function Overloading
"In a few minutes," I said, "we’ll be modifying the Grade Calculation Project by creating custom functions. Before we do that, however, there’s one more concept we need to go over, and that’s function overloading. Earlier in today’s class, I mentioned that function overloading allows you to define multiple functions with the same name, as long as each one of the functions has a different signature."
"Signature, as I recall you saying," Linda said, "is the combination of the function name along with the number and type of parameters in the function header?"
"That’s right, Linda," I said. "So long as the number and type of parameters is different in the function header, you can have any number of functions with the same name. You’d be surprised how comfortable you’ll get with function overloading as you use it more and more, and we’ll be creating some overloaded functions of our own."
"So C++ knows which of the overloaded functions to execute by examining the arguments that are passed to it?" Kate asked.
"That’s exactly right, Kate," I said. "Let me show you an example of an overloaded function. I think you’ll find it pretty interesting."
I thought for a moment, and then displayed the following code on the classroom projector:
//Example6_10.cpp
#include <iostream>
#include <string>
using namespace std;
void DisplayMessage(string favorite);
void DisplayMessage(string favorite, string intensity);
int main()
{
DisplayMessage("C++");
DisplayMessage("C++", "a lot");
return 0;
}
void DisplayMessage(string favorite)
{
cout << "I love " << favorite << endl;
}
void DisplayMessage(string favorite, string intensity)
{
cout << "I love " << favorite << " " << intensity << endl;
}
I then saved the program as Example6_10.cpp, compiled it and then executed the program. The following screenshot was displayed on the classroom projector:
Illustration 12
"That is impressive," Ward said. "I think I can see a lot of practical applications for function overloading at my work."
"This is a pretty simple example," I said, "but I think it illustrates the concept of function overloading quite nicely. What we’ve done here is define two custom functions, each one with the same name called DisplayMessage(), but with different signatures. The first function accepts a single String argument called favorite:"
void DisplayMessage(string favorite)
{
cout << "I love " << favorite << endl;
}
"And the second function accepts two String arguments called favorite and intensity:"
void DisplayMessage(string favorite, string intensity)
{
cout << "I love " << favorite << " " << intensity << endl;
}
"C++ determines which one of the two functions to execute," I said, "by matching up the number of type of arguments that are supplied with the function call. This line of code tells C++ to execute the DisplayMessage() function that requires just a single String parameter:"
DisplayMessage("C++");
"This line of code tells C++ to execute the DisplayMessage() function that requires two String parameters:"
DisplayMessage("C++", "a lot");
"Somehow," Rhonda said, "with the term ‘overloading,’ I thought this would be a lot more difficult than it turned out to be. This isn’t bad at all. In this case, a picture really was worth a thousand words."
"Are the names of the parameters within the function header considered when C++ determines which function to execute?" Dave asked. "For instance, suppose you have two identically named functions, requiring the same number and types of parameters, but the names of the parameters are different?"
"That’s a good question, Dave," I said. "The names of the parameters in the header are not considered by C++, only the numbers and types of parameters. For instance, if we were to define two functions named DisplayMessage() and specify that each one accept a single String parameter, the fact that we name the String parameter in the first function Elton and the String parameter in the second function Elvis doesn’t matter. When we compiled the program, C++ would generate a compiler error, informing us that we attempted to define a duplicate function."
"What about the return value of the function?" Linda asked. "Is that considered to be part of the function’s signature?"
"Another good question," I said, "and the answer is no. The return value is not considered to be part of the function’s signature and is not used by C++ when determining if the function can be overloaded."
"I’m not sure I understand what Linda means about the return value being considered part of the function’s signature," Chuck said.
"In other words, Chuck," I said, "let’s say you have two identically named functions, both requiring the same number and type of arguments, but one has a return value of void and the other a return value of string. Does C++ consider them to be unique? The answer is no. When you compile the class in which these two functions appear, C++ will tell you that you’re trying to define to identical functions. The bottom line is that only the number and type of parameters affects the uniqueness of an overloaded function in C++’s eyes. If you declare two functions with the same signature, you’ll generate a compiler error."
I waited for more questions, but everyone seemed satisfied with the concept of function overloading.
"With the remaining time we have left today," I said, as I glanced at the classroom clock, "I’d like to make changes to the Grade Calculation program we wrote last week by adding several custom functions to the program. You’ll also have a chance to work with an overloaded function."
I then distributed the final exercise of the day for the class to complete.
Begin Exercise
(7)Exercise 6-3: The Grade Calculation Project with Custom Functions
In this exercise, you’ll modify the Grade Calculation program by taking some of the code currently residing in the main() function and creating several custom functions: WhatKindOfStudent(), CalculateEnglishGrade(), CalculateMathGrade(), CalculateScienceGrade(), and three overloaded versions of DisplayGrade().
//Grades.cpp
#include <iostream>
#include <string>
using namespace std;
int WhatKindOfStudent();
void CalculateEnglishGrade();
void CalculateMathGrade();
void CalculateScienceGrade();
void DisplayGrade(int midterm, int finalExamGrade,
int research, int presentation,
float finalNumericGrade,
char finalLetterGrade);
void DisplayGrade(int midterm, int finalExamGrade,
float finalNumericGrade,
char finalLetterGrade);
void DisplayGrade(int midterm, int finalExamGrade,
int research,
float finalNumericGrade,
char finalLetterGrade);
const float ENGLISH_MIDTERM_PERCENTAGE = .25;
const float ENGLISH_FINALEXAM_PERCENTAGE = .25;
const float ENGLISH_RESEARCH_PERCENTAGE = .30;
const float ENGLISH_PRESENTATION_PERCENTAGE = .20;
const float MATH_MIDTERM_PERCENTAGE = .50;
const float MATH_FINALEXAM_PERCENTAGE = .50;
const float SCIENCE_MIDTERM_PERCENTAGE = .40;
const float SCIENCE_FINALEXAM_PERCENTAGE = .40;
const float SCIENCE_RESEARCH_PERCENTAGE = .20;
int midterm = 0;
int finalExamGrade = 0;
int research = 0;
int presentation = 0;
float finalNumericGrade = 0;
char finalLetterGrade;
char response[256];
string moreGradesToCalculate;
int main ()
{
int lresponse;
cout << "Do you want to calculate a grade? ";
cin >> moreGradesToCalculate;
for (int i = 0;
i < moreGradesToCalculate.length(); i++) {
moreGradesToCalculate[i] =
toupper (moreGradesToCalculate[i]);
}
while (moreGradesToCalculate == "YES") {
lresponse = WhatKindOfStudent();
switch(lresponse)
{
case 1:
CalculateEnglishGrade();
DisplayGrade (midterm, finalExamGrade,
research, presentation,
finalNumericGrade,
finalLetterGrade);
break;
case 2:
CalculateMathGrade();
DisplayGrade (midterm, finalExamGrade,
finalNumericGrade,
finalLetterGrade);
break;
case 3:
CalculateScienceGrade();
DisplayGrade (midterm, finalExamGrade,
research, finalNumericGrade,
finalLetterGrade);
break;
} // end of switch
cout << endl<< endl<<
"Do you have another grade to calculate? ";
cin >> moreGradesToCalculate;
for (int i = 0;
i < moreGradesToCalculate.length(); i++) {
moreGradesToCalculate[i] = toupper
(moreGradesToCalculate[i]);
} // end of for
} // end of while
cout <<
"Thanks for using the Grades Calculation program!";
return 0;
}
int WhatKindOfStudent()
{
int lresponse;
cout << "Enter student type " <<
"(1=English, 2=Math, 3=Science): ";
cin.getline(response,256);
if (strlen(response) == 0) {
cout << "You must select a Student Type";
exit(1);
}
if ((atoi(response) < 1) | (atoi(response) > 3)) {
cout << response <<
" - is not a valid student type";
exit(1);
}
return atoi(response);
}
void CalculateEnglishGrade()
{
cout << "Enter the Midterm Grade: " ;
cin.getline(response,256);
midterm = atoi(response);
cout << "Enter the Final Examination Grade: " ;
cin.getline(response,256);
finalExamGrade = atoi(response);
cout << "Enter the Research Grade: " ;
cin.getline(response,256);
research = atoi(response);
cout << "Enter the Presentation Grade: " ;
cin.getline(response,256);
presentation = atoi(response);
finalNumericGrade =
(midterm *
ENGLISH_MIDTERM_PERCENTAGE) +
(finalExamGrade * ENGLISH_FINALEXAM_PERCENTAGE) +
(research * ENGLISH_RESEARCH_PERCENTAGE) +
(presentation * ENGLISH_PRESENTATION_PERCENTAGE);
if (finalNumericGrade >= 93)
finalLetterGrade = 'A';
else
if ((finalNumericGrade >= 85) &
(finalNumericGrade < 93))
finalLetterGrade = 'B';
else
if ((finalNumericGrade >= 78) &
(finalNumericGrade < 85))
finalLetterGrade = 'C';
else
if ((finalNumericGrade >= 70) &
(finalNumericGrade < 78))
finalLetterGrade = 'D';
else
if (finalNumericGrade < 70)
finalLetterGrade = 'F';
}
void CalculateMathGrade()
{
cout << "Enter the Midterm Grade: " ;
cin.getline(response,256);
midterm = atoi(response);
cout << "Enter the Final Examination Grade: " ;
cin.getline(response,256);
finalExamGrade = atoi(response);
finalNumericGrade =
(midterm * MATH_MIDTERM_PERCENTAGE) +
(finalExamGrade * MATH_FINALEXAM_PERCENTAGE);
if (finalNumericGrade >= 90)
finalLetterGrade = 'A';
else
if ((finalNumericGrade >= 83) &
(finalNumericGrade < 90))
finalLetterGrade = 'B';
else
if ((finalNumericGrade >= 76) &
(finalNumericGrade < 83))
finalLetterGrade = 'C';
else
if ((finalNumericGrade >= 65) &
(finalNumericGrade < 76))
finalLetterGrade = 'D';
else
if (finalNumericGrade < 65)
finalLetterGrade = 'F';
}
void CalculateScienceGrade()
{
cout << "Enter the Midterm Grade: " ;
cin.getline(response,256);
midterm = atoi(response);
cout << "Enter the Final Examination Grade: " ;
cin.getline(response,256);
finalExamGrade = atoi(response);
cout << "Enter the Research Grade: " ;
cin.getline(response,256);
research = atoi(response);
finalNumericGrade =
(midterm * SCIENCE_MIDTERM_PERCENTAGE) +
(finalExamGrade *
SCIENCE_FINALEXAM_PERCENTAGE) +
(research * SCIENCE_RESEARCH_PERCENTAGE);
if (finalNumericGrade >= 90)
finalLetterGrade = 'A';
else
if ((finalNumericGrade >= 80) &
(finalNumericGrade < 90))
finalLetterGrade = 'B';
else
if ((finalNumericGrade >= 70) &
(finalNumericGrade < 80))
finalLetterGrade = 'C';
else
if ((finalNumericGrade >= 60) &
(finalNumericGrade < 70))
finalLetterGrade = 'D';
else
if (finalNumericGrade < 60)
finalLetterGrade = 'F';
}
void DisplayGrade(int midterm, int finalExamGrade,
int research, int presentation,
float finalNumericGrade,
char finalLetterGrade)
{
cout << endl <<
"*** ENGLISH STUDENT ***" << endl << endl;
cout << "Midterm grade is: " <<
midterm << endl;
cout << "Final Exam is: " <<
finalExamGrade << endl;
cout << "Research grade is: " <<
research << endl;
cout << "Presentation grade is: " <<
presentation << endl << endl;
cout << "Final Numeric Grade is: " <<
finalNumericGrade << endl;
cout << "Final Letter Grade is: " <<
finalLetterGrade;
} // end of DisplayGrade with 6 parameters
void DisplayGrade(int midterm, int finalExamGrade,
float finalNumericGrade,
char finalLetterGrade)
{
cout << endl<<
"*** MATH STUDENT ***" << endl << endl;
cout << "Midterm grade is: " <<
midterm << endl;
cout << "Final Exam is: " <<
finalExamGrade << endl;
cout << "Final Numeric Grade is: " <<
finalNumericGrade << endl;
cout << "Final Letter Grade is: " <<
finalLetterGrade;
} // end of DisplayGrade with 4 parameters
void DisplayGrade(int midterm, int finalExamGrade,
int research,
float finalNumericGrade,
char finalLetterGrade)
{
cout << endl <<
"*** SCIENCE STUDENT ***" << endl << endl;
cout << "Midterm grade is: " <<
midterm << endl;
cout << "Final Exam is: " <<
finalExamGrade << endl;
cout << "Research grade is: " <<
research << endl;
cout << "Final Numeric Grade is: " <<
finalNumericGrade << endl;
cout << "Final Letter Grade is: " <<
finalLetterGrade;
} // end of DisplayGrade with 5 parameters
End Exercise
Begin Discussion
(7)Discussion
"Wow, that was intense," Rhonda said. "My program works, and amazingly, I think I actually understand what we did here. Essentially, we’ve taken a bunch of code out of the main() function and put it into one of several custom functions."
"Exactly right, Rhonda," I said. "We created several custom functions: WhatKindOfGrade(), CalculateEnglishGrade(), CalculateMathGrade(), CalculateScienceGrade(), and three overloaded functions called DisplayGrade(). As much as possible, I think the program is now pretty modular, although I’m sure some of you might be able to suggest the creation of some additional functions."
"I think the program is very modular," Kate said. "We have a function to determine the type of student for whom the user wishes to calculate a final grade, three functions for the calculation for each one of the three different student types, plus three overloaded functions for the display of the grade."
"The number of lines of code in the main() function has really been reduced," I said. "The first thing we did was move the variable and constant declarations out of the main() function and convert them to Global variables and constants so that their values could be accessible to the code in each of the custom functions we created. Something else that was important is that we declared a local response variable called 'lresponse'---this variable will be used to store an integer value passed to us from the WhatKindOfStudent() function representing the student type selected by the user…"
int lresponse;
"Could we have named this variable response also?" Ward asked, "or would that in some way have conflicted with the global variable response?"
"Good question Ward," I answered. "We could have named both variables response---variables that are declared in a function take precedence over the same named Global variable---meaning that C++, if it finds a local variable with the same name as a Global variable, will use the local variable. But C++ code can be confusing enough to follow--that's why I gave the variable a different name here."
I waited a moment before continuing.
"All that really remains in the main() function," I said, "is a loop that asks the user if they want to calculate a grade and, based on their response, we execute the WhatKindOfStudent() function. After it's finished executing, the local variable lresponse contains the user's valid answer:"
while (moreGradesToCalculate == "YES") {
lresponse = WhatKindOfStudent();
"Why did you say valid answer?" Mary asked.
"The WhatKindOfStudent() function prompts the user for a number from 1 to 3 indicating the type of student for which they wish to calculate a grade," I said. "As this code did when it was in the main() function, the user's response is evaluated by the WhatKindOfStudent() function, it prompts the user if they just press the ENTER key without typing 1, 2 or 3…"
if (strlen(response) == 0) {
cout << "You must select a Student Type";
"…in addition, we now do something we didn’t do before--we immediately end the program by executing the C++ exit() function with a return value of 1…"
exit(1);
"In a similar way we evaluate the user's response to see if the number entered is less than 1 or greater than 3, if it is, we warn them that their response is invalid, and once again immediately end the program…"
if ((atoi(response) < 1) | (atoi(response) > 3)) {
cout << response <<
" - is not a valid student type";
exit(1);
"As a result, the only remaining alternative is a valid response of 1, 2 or 3, and we pass that back to the main() function via this return statement, first executing the atoi() function against the value of the response variable.."
return atoi(response);
"I understand now," Mary said. "what you meant by a valid response."
"Now," I continued, "based on the value of the lresponse variable, we then call one of the three custom functions we wrote to calculate the student’s grade and execute the overloaded DisplayGrade() function. C++ decides which one of the three overloaded functions to execute by the number and type of arguments supplied as arguments:"
switch(lresponse)
{
case 1:
CalculateEnglishGrade();
DisplayGrade (midterm, finalExamGrade,
research, presentation,
finalNumericGrade,
finalLetterGrade);
break;
"Each one of the three Calculate functions is fairly well encapsulated," I said.
"Encapsulated?" Kathy asked.
"Encapsulated," I said, "means that everything that is needed to perform the calculations, including prompting the user for the component pieces of the grade, is included in the function. Not all programmers would write these functions like this. Some might very well include a separate function to prompt the user for the grade components, then execute one of the calculate functions, followed by the display functions."
"Why is that?" Ward asked. "Is there something wrong with the way we’ve done it?"
"There’s a science and art to designing functions," I said. "No two people are likely to write their program in the same way, which is one of the things I love about teaching programming. For instance, some programmers would argue that our use of Global variables is wrong. The problem is that the techniques we need to use to avoid using Global variables we are a week or two away from learning. Our ultimate use of C++ objects will make our program much more efficient--and stylistically more pleasing to the C++ programming purists."
"Something I found pretty interesting," Joe said, "is how you chose to create overloaded functions to display the grades. Why didn’t you just display the grades from within the various calculate functions?"
"Calculating a grade and displaying a grade are different tasks," I said. "Separating the code for each task makes sense, especially if you consider the fact that the manner in which we are displaying the information for Frank Olley is completely arbitrary. He doesn’t really care how the display of the information looks."
"In other words," Dave said smiling, "he may want it changed as soon as he sees it. Placing the code to display the grades in functions separate from the calculation code will make modifying the code easier."
"But why not just go with three uniquely named functions?" Ward asked. "Why did you use overloaded functions to display the grades?"
"That’s simple, Ward," I said. "I wanted to give you experience working with overloaded functions. That experience will come in handy later on in the course."
End Discussion
It had been an extremely long and interesting, class. No one had any further questions, so I dismissed class for the day.
In this chapter, you learned about the concept of program modularity and the benefits of creating custom functions in our C++ programs. We discussed details of creating our own functions, including the four types of access modifiers, return types of functions, and how to define functions to accept one or more parameters. You also learned how overloaded functions permit you to define more than one function with the same name, provided the function headers are unique in terms of number and type of arguments supplied. We finished the chapter by modifying the Grade Calculation Project to include several custom functions, including an overloaded function.