Chapter 6

Dividing Your Work with Functions

IN THIS CHAPTER

check Working with functions

check Writing your own great functions

check Fun with strings

check Manipulating main()

People generally agree that most projects throughout life are easier when you divide them into smaller, more manageable tasks. That’s also the case with computer programming — if you break your code into smaller pieces, it becomes more manageable.

C++ provides many ways to divide code into smaller portions. One way is through the use of what are called functions. A function is a set of lines of code that performs a particular job. In this chapter, you discover what functions are and how you can use them to make your programming job easier.

Remember You don’t have to type the source code for this chapter manually. In fact, using the downloadable source is a lot easier. You can find the source for this chapter in the \CPP_AIO4\BookI\Chapter06 folder of the downloadable source. See the Introduction for details on how to find these source files.

Dividing Your Work

If you have a big job to do that doesn’t involve a computer, you can divide your work in many ways. Over the years of studying process management, people have pretty much narrowed the division of a job to two ways: using nouns and using verbs.

Yes, that’s right. Back to good old English class, where everyone learned about nouns and verbs. The idea is this: Suppose that you’re going to go out back and build a flying saucer. You can approach the designing of the flying saucer in two ways.

First, you could just draw up a plan of attack, listing all the steps to build the flying saucer from start to finish. That would, of course, be a lot of steps. But to simplify it, you could instead list all the major tasks without getting into the details. It might go something like this:

  1. Build the outer shell.
  2. Build and attach the engine.

That’s it. Only two steps. But when you hire a couple dozen people to do the grunt work for you while you focus on your day trading, would that be enough for them to go on? No, probably not. Instead, you could divide these two tasks into smaller tasks. For example, Step 2 might look like this:

2a. Build the antigravity lifter.

2b. Build the thruster.

2c. Connect the lifter to the thruster to form the final engine.

2d. Attach the engine to the outer shell.

That’s a little better; it has more detail. But it still needs more. How do you do the “Build the antigravity lifter” part? That’s easy, but it requires more detail, as in the following steps:

2aa. Unearth the antigravity particles from the ground.

2ab. Compress them tightly into a superatomizing conductor.

2ac. Surround with coils.

2ad. Connect a 9-volt battery clip to the coils.

And, of course, each of these instructions requires even more detail. Eventually, after you have planned the whole thing, you will have many, many steps, but they will be organized into a hierarchy of sorts, as shown in Figure 6-1. In this drawing, the three dots represent places where other steps go — they were left off so that the diagram could fit on the page.

This type of design is a top-down design. The idea is that you start at the uppermost step of your design (in this case, “Build flying saucer”) and continue to break the steps into more and more detailed steps until you have something manageable. For many years, this was how computer programming was taught.

Schematic illustration of dividing a process into a hierarchy.

FIGURE 6-1: Dividing a process into a hierarchy.

Although this process works, people have found a slightly better way. First, before breaking the steps (which are the verbs), you divide the thing you’re building into parts (the nouns). In this case, you kind of do that already, in the first two steps. But instead of calling them steps, you can call them objects. One object is the outer shell, and one object is the engine. This way, two different factories can work on these in sort of a division of labor. Of course, the factories would have to coordinate their activities; otherwise, the two parts may not fit together when they’re ready to go. And before you figure out exactly how to build each of these objects, it would be a good idea to describe each object: what it does, its features, its dimensions, and so on. Then, when you finally have all that done, you can list the exact features and their details. And finally, you can divide the work with each person designing or building a different part.

As you can see, this second approach makes more sense. And that’s the way programmers divide their computer applications. But at the bottom of each method is something in common: The methods are made of several little processes. These processes are called functions. When you write a computer application, after you divide your job into smaller pieces called objects, you eventually start giving these objects behaviors. And to code these behaviors, you do just as you did in the first approach: You break them into manageable parts, again, called functions. In computer programming terms, a function is simply a small set of code that performs a specific task. But it’s more than that: Think of a function as a machine. You can put one or more things into the machine; it processes them, and then it spits out a single answer, if anything at all. One of the most valuable diagrams you can have draws a function in this manner, like a machine, as shown in Figure 6-2.

Schematic illustration of thinking of a function as a machine.

FIGURE 6-2: You can think of a function as a machine.

This machine (or function) has three main parts:

  • Inputs: The function can receive data through its inputs. These data elements can be numbers, strings, or any other type. When you create such a machine, you can have as many inputs as you want (or even zero, if necessary).
  • Processor: The processor is the function itself. In terms of C++, this is actually a set of code lines.
  • Output: A function can return something when it has finished doing its thing. In C++, this output is in the form of numbers, strings, or any other type.

To make all this clear, try out the FirstFunction code in Listing 6-1. (Don’t forget the second line, #include<math.h>, which gives you some math capabilities.)

LISTING 6-1: Seeing a Function in Action

#include <iostream>
#include <math.h>

using namespace std;

int main()
{
cout << fabs(-10.5) << endl;
cout << fabs(10.5) << endl;
return 0;
}

When you run this application, you see the following output:

10.5
10.5

In this code, you use a function or machine called fabs() (usually pronounced “ef-abs,” for floating-point absolute). This function takes a number as input and returns as output the absolute value of the number.

Remember The absolute value of a number is simply the positive version of the number. The absolute value, for example, of –5 is simply 5. The absolute value of 12 is still 12. An absolute value is almost always positive because the absolute value of 0 is 0, but 0 is the origin, which is neither positive nor negative (see http://www.math.com/school/subject1/lessons/S1U1L10DP.html for details). The reason for the f before the name abs is that it uses floating-point numbers, which are simply numbers with decimal points.

So the first line inside main() calls fabs() for the value –10.5. The cout then takes the output of this function (that is, the result) and prints it to the console.

Then the second line does the same thing again, except that it takes the absolute value of the number 10.5.

And where is the processor for this function? It’s not in your code; it’s in another file, and the following line ensures that your application can use this function:

#include <math.h>

You have seen functions in many places. If you use a calculator and enter a number and press the square root button, the calculator runs a function that calculates the square root.

But functions can be more sophisticated than just working with numbers. Consider this statement carefully: When you are using a word processor and you highlight a word and check the spelling of the word, the application calls a function that handles the spelling check. This function does something like the following:

This is a function to check the spelling of a single word.
Inputs: A single word.
Look up the word
If the word is not found
Find some suggestions.
Open a dialog box through which you (the user)
can change the word by either typing a new word
or picking one of the selections, or just leaving
it the same.
If you made a change,
Return the new spelling.
Otherwise
Return nothing.
Otherwise
Return nothing

Notice how the if statements are grouped with indentations. The final otherwise goes with the first if statement because its indentation matches that of the if statement.

So that’s a function that performs a spelling check. But consider this: When you do not highlight a word but run the spelling checker, the spelling checker runs for the whole document. That’s another function. Here it is.

This function checks the spelling of the entire document
For each word in the document
Check the spelling of the single word

How does the computer do the step inside the for loop, “Check the spelling of the single word?” It calls the function described earlier. This process is called code reuse. You have no reason to rewrite the entire function again if you already have it somewhere else. And that’s the beauty of functions.

Calling a Function

When you run the code in a function, computer people say that you are calling the function. And just like every good person, a good function has a name. When you call a function, you do so by name.

Tip Often, when writing an application and developing code to call a function, developers say that they are calling a function. This is partly computerspeak and partly a strange disorder in which developers start to relate just a little too much to the computer.

To call a function, you type its name and then a set of parentheses. Inside the parentheses, you list the items you want to send to the inputs of the function. The term used here is pass, as in “You pass the values to the function.”

For example, if you want to call the fabs() function, you type the name, fabs, an open parenthesis, the number you want to pass to it, and then a closed parenthesis, as in the following example:

fabs(-10.5)

But by itself, this line does not do anything with regard to the application as a whole. The fabs() function returns a value — the absolute value of –10.5, which comes out to be 10.5 — and you probably want to do something with that value. You could, for example, print it to the console:

cout << fabs(-10.5) << endl;

Or you could store it away in another variable. But there’s a catch. Before you can do that, you need to know the type that the function returns. Just as with a variable, a function return value has a type. In this case, the type is a special type called double (which stands for double precision floating point). The double type is a floating-point type that can hold many digits in a single number. To save the result of fabs(), you need to have a variable of type double. The Fabs2 example, shown in Listing 6-2, does this.

LISTING 6-2: Seeing Another Way to Use fabs()

#include <iostream>
#include <math.h>

using namespace std;

int main()
{
double mynumber = fabs(-23.87);
cout << mynumber << endl;
return 0;
}

This code declares a double variable called mynumber. Then it calls fabs(), passing it –23.87 and returning the value into mynumber. Next, it prints the value in mynumber to the console.

When you run this application, you see the following, which is the absolute value of –23.87:

23.87

Passing a variable

You can also pass the value of a variable into a function. The Fabs3 example in Listing 6-3 creates two variables: One is passed into the function, and the other receives the result of the function.

LISTING 6-3: Seeing Yet Another Way to Use fabs()

#include <iostream>
#include <math.h>

using namespace std;

int main()
{
double start = -253.895;
double finish = fabs(start);
cout << finish << endl;
return 0;
}

This code creates two variables; the first is called start, and the second is called finish. It initializes start with a value of -253.895. Next, it calls fabs(), passing it the value of start. It saves the return value in finish, and prints the value in finish. When Fabs3 runs, you see the following appear on the console:

253.895

Tip Saving a function result to a variable is useful if you need to use the result several times over. For example, if you need the absolute value of –253.895 for whatever reason and then a few lines later you need it again, you have a choice: You can either call fabs(-253.895) each time or call it once, save the result in a variable, and then use the variable each time you need it. The advantage to saving it in a variable is that you might later say, for example, “Oh, wait! I didn’t just want the absolute value! I wanted the negative of the absolute value!” Then you only have to change one line of code — the line where it calls fabs(). If, instead, you had called fabs() several times, you would have had to change it every time you called it. And by the way, in case you’re curious about how to take the negative of the absolute value and store it in a variable, you just throw a minus sign in front of it, like so:

finish = -fabs(start);

Passing multiple variables

Some functions like to have all sorts of goodies thrown their way, such as multiple parameters. As with functions that take a single value, you put the values inside a single set of parentheses. Because you have multiple values, you separate them with commas. The Pow1 example, shown in Listing 6-4, uses a function called pow() to calculate the third power of 10. (That is, it calculates 10 times 10 times 10. Yes, POW!). Make sure that you include the math.h line in the include section so that you can use the pow() function.

LISTING 6-4: Seeing Yet One More Function in Action

#include <iostream>
#include <math.h>

using namespace std;

int main()
{
double number = 10.0;
double exponent = 3.0;
cout << pow(number, exponent) << endl;
return 0;
}

When you run the application, you see 10 to the third power, which is 1,000:

1000

You can also pass a mixture of variables and numbers, or just numbers. The following code snippet also calculates the third power of 10 but passes an actual number, 3.0, for the power:

double number = 10.0;
cout << pow(number, 3.0) << endl;

Or you can pass only numbers:

cout << pow(10.0, 3.0) << endl;

Writing Your Own Functions

And now the fun begins! Calling functions is great, but you get real power (ooh!) when you write your own, specialized functions. Before writing a function, remember the parts: the inputs, the main code or processor, and the single output (or no output). The inputs, however, are called parameters, and the output is called a return value. The following sections fill you in on the details.

Defining the AddOne() function

The AddOne example, shown in Listing 6-5, provides both a custom function and code in main() that calls the custom function. (The function is placed outside main() — before it, in fact.)

LISTING 6-5: Writing Your Very Own Function

#include <iostream>

using namespace std;

int AddOne(int start)
{
int newnumber = start + 1;
return newnumber;
}

int main()
{
int testnumber = 20;
int result = AddOne(testnumber);
cout << result << endl;
return 0;
}

Remember Notice that this example lacks the #include <math.h> entry found in earlier examples. You need to add an entry to the include section of your code only when you use a feature of that include file. In this case, the example relies on standard math features that are part of the basic C++ language, so you don’t need any additional code.

Using the downloadable source will save you time and ensure that the example runs the first time you try it. However, you might choose to type it manually. Because there’s a good bit of code, you may get some compiler errors at first; look carefully at the lines with the errors and find the difference between your code and what’s here in the book. After you run the example, you see:

21

Seeing how AddOne() is called

You can start reviewing this code by seeing how to call AddOne(). Look at these lines of main():

int testnumber = 20;
int result = AddOne(testnumber);
cout << result << endl;

You can probably put together some facts and determine what the function does. First, the example is called AddOne(), which is a good indication in itself. Second, when you run the application, the number 21 appears on the console, which is one more than the value in testnumber; it adds one. And that, in fact, is what the function does. It’s amazing what computers can do these days.

Tip When you write your own functions, try to choose a name that makes sense and describes what the function does. Writing a function and calling it something like process() or TheFunction() is easy, but those names don’t accurately describe the function.

Taking the AddOne() Function apart

Now, look at the AddOne function. Here are a few high-level observations about it:

  • Position: The function appears before main(). Because of the way the compiler works, it must know about a function before you call it. And thus, you put it before main(). (You can do this in another way that is discussed in the “Forward references and function prototypes” section, later in this chapter.)
  • Format: The function starts with a line that seems to describe the function (explained later in this section), and then it has an open brace and, later, a closing brace.
  • Code: The function has code in it that is just like the type of code you could put inside a main(). The code consists of these elements:
    • Performing a task: The code begins by performing a task, like this:

      int newnumber = start + 1;

    • The code declares an integer variable called newnumber. Then it initializes it to start plus 1. But what is start? That’s one of the inputs.
    • Returning a result: This line appears at the end of the function:

      return newnumber;

    • This is the output of the function, or the return value. When you want to return something from a function, you just type the word return and then indicate what you want to return. From the first line in the AddOne() function, you can see that newnumber is one more than the number passed into the function. So this line returns the newnumber.

Considering the AddOne() parameter

AddOne() takes just one parameter called start, which comes from the first line of the function:

int AddOne(int start)

The entry in parentheses is the parameter. Notice it looks like a variable declaration; it’s the word int (the type, or integer) followed by a variable name, start. That’s the parameter — the input — to the function, and you can access this parameter throughout the function using a variable called start. You can use the input to the function as a variable.

If you had written result = AddOne(25); in main(), then, throughout the function, the value of start would be 25. Likewise, if you had written

result = AddOne(152);

then, throughout the function, the value of start would be 152.

But here’s the outstanding thing about functions (or, at least, one of the loads of outstanding things about functions): You can call the function several times over. In the same main(), you can have the following lines

cout << AddOne(100) << endl;
cout << AddOne(200) << endl;
cout << AddOne(300) << endl;

which would result in this output:

101
201
301

In the first call to AddOne, the value of start would be 100. During the second call, the value would be 200, and during the third call, it would be 300.

Understanding the AddOne() name and type

Look at the AddOne() header again:

int AddOne(int start)

The word AddOne is the name of the function, as you’ve probably figured out already. And that leaves the thing at the beginning — the int. That’s the type of the return value. The final line in the function before the closing brace is

return newnumber;

The variable newnumber inside the function is an integer. And the return type is int. That’s no accident: As programmers have all heard before, friends don’t let friends return something other than the type specified in the function header. The two must match in type. And further, examine this line from inside main():

int result = AddOne(testnumber);

The type of result is also an integer. All three match. Again, no accident. You can copy one thing to another (in this case, the function’s return value to the variable called result) only if they match in type. And here, they do — they’re both integers.

Notice one more thing about the function header: It has no semicolon after it. This is one of the places you do not put a semicolon. If you do, the compiler gets horribly confused. The Code::Blocks compiler shows an error that says, "error: expected unqualified-id before ’{’ token."

Finally, ponder this line of code for a moment:

testnumber = AddOne(testnumber);

This line takes the value stored inside testnumber, passes it into AddOne(), and gets back a new number. It then takes that new number and stores it back into testnumber. Thus, testnumber’s value changes based on the results of the function AddOne().

Improving On the Basic Function

Not all functions work precisely the same way. You can create functions that have multiple parameters or no parameters. There is no law that says that a function must absolutely provide a return value. The following sections discuss variations on the basic function theme discussed in the previous section.

Using multiple parameters or no parameters

You don’t need to write your functions with only one parameter each. You can have several parameters, or you can have none. It may seem a little strange that you would want a function — a machine — that accepts no inputs. But you may run into lots of cases where this may be a good idea. Here are some ideas for functions:

  • Day: Determines the day and returns it as a string, as in "Monday" or "Tuesday"
  • Number-of-users: Figures out the current number of users logged in to a web-server computer
  • Current font: In a text editor application (such as Notepad), returns a string containing the current font name, such as "Arial"
  • Editing time: Returns the amount of time you have been using the word processor application
  • Username: If you are logged on to a computer, gives back your username as a string, such as "Elisha"

All functions in this list have something in common: They look up information. Because no parameters are in the code, for the functions to process some information, they have to go out and get it themselves. It’s like sending people out into the woods to find food but not giving them any tools: It’s totally up to them to perform the required tasks, and all you can do is sit back and watch and wait for your yummy surprise.

If a function takes no parameters, you write the function header as you would for one that takes parameters, and you include the parentheses; you just don’t put anything in the parentheses, as the UserName example in Listing 6-6 shows. So if nothing good is going in, there really can be something good coming back out, at least in the case of a function with no parameters.

LISTING 6-6: Taking No Parameters

#include <iostream>

using namespace std;

string Username()
{
return "Elisha";
}

int main()
{
cout << Username() << endl;
return 0;
}

When you run Listing 6-6, you see the following output:

Elisha

Your function can also take multiple parameters. The ConnectNames example, shown in Listing 6-7, demonstrates the use of multiple parameters. Notice that the function, ConnectNames(), takes the two strings as parameters and combines them, along with a space in the middle. Notice also that the function uses the two strings as variables.

LISTING 6-7: Taking Multiple Parameters

#include <iostream>

using namespace std;

string ConnectNames(string first, string last)
{
return first + " " + last;
}

int main()
{
cout << ConnectNames("Richard", "Nixon") << endl;
return 0;
}

In the function header in Listing 6-7, you see the type name string for each parameter. Each parameter requires its own type entry or the compiler displays an error. Here are some points about this code:

  • You didn’t create variables for the two names in main(). Instead, you just typed them as string constants (that is, as actual strings surrounded by quotes).
  • You can do calculations and figuring right inside the return statement. That saves the extra work of creating a variable. In the function, you could create a return variable of type string, set it to first + " " + last, and then return that variable, as in the following code:

    string result = first + " " + last;
    return result;

    But instead, the example shows how to do it all on one line, as in this line:

    return first + " " + last;

Although you can save yourself the work of creating an extra variable and just put the whole expression in the return statement, sometimes that’s a bad thing. If the expression is really long, like the following:

return (mynumber * 100 + somethingelse / 200) *
(yetanother + 400 / mynumber) / (mynumber + evenmore);

it can get just a tad complicated. Breaking it into variables, such as in this example, is best:

double a = mynumber * 100 + somethingelse / 200;
double b = yetanother + 400 / mynumber;
double c = mynumber + evenmore;
return a * b / c;

Returning nothing

In the earlier section “Using multiple parameters or no parameters,” you see a list of functions that take no parameters; these functions go and bring back something, whether it’s a number, a string, or some other type of food.

One such example gets the username of the computer you’re logged in to. But what if you are the great computer guru, and you are writing the application that actually logs somebody in? In that case, your application doesn’t ask the computer what the username is — your application tells the computer what the username is, by golly!

In that case, your application would call a function, like SetUsername(), and pass the new username. The resulting function could do any of the following for a return value:

  • It could return the name
  • It could return a message saying that the username is not valid or something like that
  • It may not return anything at all

Look at the case in which a function doesn’t return anything. In C++, the way you state that the function doesn’t return anything is by using the keyword void as the return type in the function header. The SetUserName example, shown in Listing 6-8, demonstrates this approach.

LISTING 6-8: Returning Nothing at All

#include <iostream>

using namespace std;

void SetUsername(string newname)
{
cout << "New user is " << newname << endl;
}

int main()
{
SetUsername("Harold");
return 0;
}

When you run the application, you see

New user is Harold

Notice the SetUsername() function header: It starts with the word void, which means that it returns nothing at all. It’s like outer space: There’s just a big void with nothing there, and nothing is returned, except for static from the alien airwaves, but we won’t go there. Also notice that, because this function does not return anything, there is no return statement.

Now, of course, this function really doesn’t do a whole lot other than print the new username to the console, but that’s okay; it shows you how you can write a function that does not return anything.

Remember A function of return type void returns nothing at all.

Do not try to return something in a function that has a return type of void. Void means that the function returns nothing at all. If you try to put a return statement in your function, you get a compile error.

Keeping your variables local

Everybody likes to have their own stuff, and functions are no exception. When you create a variable inside the code for a function, that variable will be known only to that particular function. When you create such variables, they are called local variables, and people say that they are local to that particular function. (Well, computer people say that, anyway.)

To see a local variable at work, consider the code in the PrintName example:

#include <iostream>

using namespace std;

void PrintName(string first, string last)
{
string fullname = first + " " + last;
cout << fullname << endl;
}

int main()
{
PrintName("Thomas", "Jefferson");
return 0;
}

Notice in the PrintName() function that you declare a variable called fullname. You then use that variable in the second line in that function, the one starting with cout. But you cannot use the variable inside main(). If you try to, as in the following code, you get a compile error:

int main()
{
PrintName("Thomas", "Jefferson");
cout << fullname << endl;
return 0;
}

However, you can declare a variable called fullname inside main(), as in the PrintName2 example. But, if you do that, this fullname is local only to main(), whereas the other variable, also called fullname, is local only to the PrintName() function. In other words, each function has its own variable; they just happen to share the same name. But they are two separate variables:

#include <iostream>

using namespace std;

void PrintName(string first, string last)
{
string fullname = first + " " + last;
cout << fullname << endl;
}

int main()
{
string fullname = "Abraham Lincoln";
PrintName("Thomas", "Jefferson");
cout << fullname << endl;
return 0;
}

Remember When two functions declare variables by the same name, they are two separate variables. If you store a value inside one of them, the other function does not know about it. The other function only knows about its own variable by that name. Think of it this way: Two people could each have a storage bin labeled Tools in their closet. If Sally puts a hammer in her bin labeled Tools at her house and Hal opens another bin also labeled Tools at his house, he won’t see Sally’s hammer. As a result, the output from this example is:

Thomas Jefferson
Abraham Lincoln

Warning If you use the same variable name in two different functions, forgetting that you are working with two different variables is very easy. Do this only if you are sure that no confusion can occur.

Tip If you use the same variable name in two different functions (such as a counter variable called index, which you use in a for loop), matching the case is usually a good idea. Don’t use count in one function and use Count in another. Although you can certainly do that, you may find yourself typing the name wrong when you need it. But that won’t cause you to access the other one. (You can’t, because it is in a different function.) Instead, you get a compile error, and you have to go back and fix it. Being consistent is a time-saver.

Forward references and function prototypes

All examples in this chapter place the function code above the code for main(). The reason is that the compiler scans the code from start to finish. If it has not yet encountered a function but sees a call to it, it doesn’t know what it’s seeing, and it issues a good old compile error.

Such an error can be especially frustrating and can cause you to spend hours yelling at your computer. Nothing is more frustrating than looking at your application and being told by the compiler that it’s wrong, yet knowing that it’s correct because you know that you wrote the function.

You can, however, place your functions after main(); or you can even use function prototypes to put your functions in other source code files (a topic you find in Book 1, Chapter 7).

What you can do is include a function prototype. A function prototype is nothing more than a copy of the function header. But rather than follow it with an open brace and then the code for the function, you follow the function header with a semicolon and you are finished. A function prototype, for example, looks like this:

void PrintName(string first, string last);

Then you actually write the full function (header, code, and all) later. The full function can even be later than main() or later than any place that makes calls to it.

Notice that this example looks just like the first line of a function. In fact, it’s possible to cheat! To write it, you simply copy the first line of the original function you write and add a semicolon. The PrintName3 example, shown in Listing 6-9, shows how to use this technique.

LISTING 6-9: Using a Function Prototype

#include <iostream>

using namespace std;

void PrintName(string first, string last);

int main()
{
PrintName("Thomas", "Jefferson");
return 0;
}

void PrintName(string first, string last)
{
string fullname = first + " " + last;
cout << fullname << endl;
}

Notice that the function header appears above main() and ends with a semicolon. Next comes main(). Finally, you see the PrintName() function itself (again, with the header but no semicolon this time). Thus, the function comes after main().

“Whoop-de-do,” you say. “The function comes after.” But why bother when now you have to type the function header twice?

This step truly is useful. If you have a source code file with, say, 20 functions, and these functions all make various calls to each other, it could be difficult to carefully order them so that each function calls only functions that are above it in the source code file. Instead, most programmers put the functions in some logical order (or maybe not), and they don’t worry much about the calling order. Then they have all the function prototypes toward the top of the source code file, as shown previously in Listing 6-9.

Tip When you type a function prototype, many people say that you are specifying a forward reference. This phrase simply means that you are providing a reference to something that happens later. It’s not a big deal, and it mainly comes from some of the older programming languages.

Writing two versions of the same function

Sometimes you may want to write two versions of the same function, with the only difference being that they take different parameter types. For example, you may want a function called Combine(). One version takes two strings and puts the two strings together, but with a space in the middle. It then prints the resulting string to the console. Another version adds two numbers and writes all three numbers — the first two and the sum — to the console. The first version would look like this:

void Combine(string first, string second)
{
cout << first << " " << second << endl;
}

There’s nothing magical or particularly special about this function. It’s called Combine(); it takes two strings as parameters; it doesn’t return anything. The code for the function prints the two strings with a space between them. Now the second version looks like this:

void Combine(int first, int second)
{
int sum = first + second;
cout << first << " " << second << " " << sum << endl;
}

Again, nothing spectacular here. The function name is Combine(), and it doesn’t return anything. But this version takes two integers, not two strings, as parameters. The code is also different from the previous code in that it first computes the sum of the inputs and then prints the different numbers.

Remember Overloading, or using one name for multiple functions, is somewhat common in C++. The Combine example, shown in Listing 6-10, contains the entire code. Both functions are present in the listing.

LISTING 6-10: Writing Two Versions of a Function

#include <iostream>

using namespace std;

void Combine(string first, string second)
{
cout << first << " " << second << endl;
}

void Combine(int first, int second)
{
int sum = first + second;
cout << first << " " << second << " " << sum << endl;
}

int main()
{
Combine("David","Letterman");
Combine(15,20);
return 0;
}

You see each function called in main(). The compiler chooses which function to call based on the arguments you provide. For example, when viewing this call:

Combine("David","Letterman");

you see two strings. So, the compiler knows to use the first version, which takes two strings. Now look at the second function call:

Combine(15,20);

This call takes two integers, so the compiler knows to use the second version of the function.

Remember When you overload a function, the parameters must differ (or must appear in a different order). For example, the functions can take the same type of information but use a different number of parameters. Of course, the previous example shows that the parameters can also vary by type. You can also have different return types, though they must differ by more than just the return type, and varying the parameter names doesn’t count. The compiler will see Combine(string A, string B) and Combine(string First, string Second) as the same function.

Calling All String Functions

To get the most out of strings, you need to make use of some special functions that cater to the strings. However, using these functions is a little different from the other functions used so far in this chapter. Rather than just call the function, you first type the variable name that holds the string, and then a period (or dot), and then the function name along with any arguments.

Technical stuff The reason you code string functions differently is because you’re making use of some object-oriented programming features. Book 2, Chapter 1 describes in detail how these types of functions (called methods) work. The following sections describe some common functions and tell you how to use them.

Inserting a string into a string

One function that you can use is insert(). You can use this function if you want to insert more characters into another string. For example, if you have the string "Something interesting and bizarre" and you insert the string "seriously " (with a space at the end) into the middle of it starting at index 10, you get the string "Something seriously interesting and bizarre".

Remember When you work with strings, the first character is the 0th index, and the second character is the 1st index, and so on. The following lines of code perform an insert by using the insert() function at index 10, even though you perform the insertion at letter 11:

string words = "Something interesting and bizarre";
words.insert(10, "seriously ");

The first of these lines simply creates a string called words and stuffs it full with the phrase "Something interesting and bizarre". The second line does the insert. Notice the strange way of calling the function: You first specify the variable name, words, and then a dot, and then the function name, insert. Next, you follow it with the parameters in parentheses, as usual. For this function, the first parameter is the index where you want to insert the string. The second parameter is the actual string you are going to insert. After these two lines run, the string variable called words contains the string "Something seriously interesting and bizarre".

Removing parts of a string

You can also erase parts of a string by using a similar function called erase(). The following line of code erases 16 characters from the words string starting at index 19:

words.erase(19,16);

Consequently, if the variable called words contains the string "Something seriously interesting and bizarre", after this line runs, it will contain "Something seriously bizarre".

Replacing parts of a string

Another useful function is replace(). This function replaces a certain part of the string with another string. To use replace, you specify where in the string you want to start the replacement and how many characters you want to replace. Then you specify the string with which you want to replace the old, worn-out parts.

For example, if your string is "Something seriously bizarre" and you want to replace the word "thing" with the string "body", you tell replace() to start at index 4 and replace 5 characters with the word "body". To do this, you enter:

words.replace(4, 5, "body");

Tip Notice that the number of characters you replace does not have to be the same as the length of the new string. If the string starts out with "Something seriously bizarre", after this replace() call the string contains "Somebody seriously bizarre".

Using the string functions together

The OperatingOnStrings example, shown in Listing 6-11, demonstrates all these functions working together.

LISTING 6-11: Operating on Strings

#include <iostream>

using namespace std;

int main()
{
string words = "Something interesting and bizarre";
cout << words << endl;
words.insert(10, "seriously ");
cout << words << endl;
words.erase(19,16);
cout << words << endl;
words.replace(4, 5, "body");
cout << words << endl;
return 0;
}

When you run this application, you see the following output:

Something interesting and bizarre
Something seriously interesting and bizarre
Something seriously bizarre
Somebody seriously bizarre

The first line is the original string. The second line is the result of the insert() function. The third line is the result of the erase() function. And the final line is the result of the replace() function.

Understanding main()

All applications so far in this chapter have had a main(), which is a function. Notice its header, which is followed by code inside braces:

int main()

You can see that this is definitely a function header: It starts out with a return type and then the function name, main(). This is just one form of the main() function — the form that Code::Blocks uses by default. However, you may decide that you want to give users the ability to provide input when they type the name of your application at the console. In this case, you use this alternative form of the main() function that includes two parameters:

int main(int argc, char *argv[])

Notice that the second form of main() has two parameters:

  • int argc: Tells you how many arguments appear on the command line.
  • char *argv[]: Provides a list of the command-line arguments in an array.

A command-line argument is something you type in the Windows Command Prompt or at the Linux Terminal window after the name of the application (the command you want to execute). When you run an application, especially from the command prompt, you type the name of the application and press Enter. But before pressing Enter, you can follow the application name with other words that are generally separated by spaces.

Many of the commands you use in Terminal window and the Command Prompt have an application name and then various arguments. The command usually tells you about these arguments when you enter a special argument such as /? or --h. An argument preceded by a slash (/) or two dashes (--) is a switch because it affects how the command works. Figure 6-3 shows an example of the dir (directory) command using the /? switch to tell you about the other arguments (including other switches) available with dir.

Snapshot of the command-line apps often have switches and arguments.

FIGURE 6-3: Command-line apps often have switches and arguments.

To make these switches and their associated arguments work, the main() function must process the input. You determine how many command-line arguments the user supplied using argc, and then access them using argv. Book 2, Chapter 2 deals with the topic arrays. An array is a sequence of variables stored under one name. The argv variable is one such animal. To access the individual variables stored under the single umbrella known as argv, you do something like this:

cout << argv[0] << endl;

In this example, you use brackets as you did when accessing the individual characters in a string. When working with the /? switch, you see /? as the output. You can access the command-line parameters using a for loop. The CommandLineParameters example, shown in Listing 6-12, demonstrates this technique.

LISTING 6-12 Accessing the Command-Line Parameters

#include <iostream>
#include <stdlib.h>

using namespace std;

int main(int argc, char *argv[])
{
for (int index=1; index < argc; index++)
{
cout << argv[index] << endl;
}

return 0;
}

Remember Notice that the for loop begins at index = 1 rather than index = 0. The first item in the argv list is always the execution path and the name of the application. This information can come in handy at times, but normally you want the remaining arguments to change the way your application works.