5. Defining Your Own Functions

The premise behind any function in any language is simple: encapsulate a process under one name. Then, by calling that name (i.e., invoking the function), you can repeat that sequence of steps with ease. You’ve already seen this in action when you use the functions defined in C++, like cin.get() or the close() function used with files. In this chapter you’ll learn how to define and call your own functions.

The syntax for defining a function is simple and consistent, but there are many permutations as to what a function can do. For starters, you’ll create a simple function, one that takes no arguments and returns no values. Then you’ll begin writing functions that take required arguments, and then those that take optional arguments. After that, you’ll define functions that actually return a value, like the result of a computation. From these rudimentary concepts, the chapter delves into the more sophisticated notion of overloading functions. The chapter concludes with a lesson on variable scope: understanding where variables exist. As you go through the chapter, you’ll also see a few other tidbits about related topics, like inline functions, recursive functions, and other variable designations.

Creating Simple Functions

There are two steps to defining a function in C++. First you establish the function’s syntax, using a function prototype. The prototype indicates the function’s name, what type and number of arguments it takes, if any, and what kind of value, if any, it returns. The prototype does not include the actual content of the function itself. The prototype is necessary so that the compiler can confirm that a function is being used correctly when it is called (the compiler will see the prototype, then the invocation of the function).

The syntax for the prototype is

typeReturned functionName (arguments);

If the function returns no value, its typeReturned will be void. All of the functions defined in this chapter will only ever return a simple numeric type, like int or float. In the next two chapters, you’ll learn about, and have functions return, more complex types. (As an aside, it makes no difference whether you have a space between the function’s name and its opening parentheses; in this book we will sometimes add a space to make the result more legible.)

The rules for naming functions are the same as those for naming variables: you use alphanumeric characters plus the underscore. Remember that function names are case-sensitive in C++, so you should stick to a consistent capitalization strategy. Also, as with variables, you cannot use an existing keyword for your functions (like int or break, and so on). You can use an existing function’s name, as you’ll learn about later in the chapter, but until you understand when and why you might do that, you should stick with unique and obvious function names.

The arguments that a function takes make for a more complicated subject, which will be covered in a few pages. For now, keep in mind that if a function takes no arguments, the parentheses are left blank.

With all of this information in mind, this function prototype defines a function called sayHello that takes no arguments and returns no values:

void sayHello();

The second step in creating your own functions, after declaring the prototype, is to actually define the function itself. This is also called implementing the function. The syntax starts exactly like the prototype but concludes with the body where the actual stuff happens.

void sayHello() {
    std::cout << "Hello.";
}

Once your function is prototyped and defined, you call it like any other:

sayHello();

Before running through an example, let’s take a look at how a whole C++ file would be arranged. A function’s prototype is normally placed before the definition of the main() function. This way, the compiler already knows the function’s syntax when it examines the function call within main(). After the main() function, the user-defined functions are then fully listed.

Your C++ file will look something like this:

#include statements
function prototypes
int main() {
    // Do whatever.
    // Call your functions.
}
function definitions

The question that you are hopefully asking by this point is “When should I define my own functions?” Good question! In general, you’ll want to define your own function when you have a sequence of code—some sort of process—that you repeatedly use or could be naturally separated out. You should not define your own function if it already exists in C++!

You’ll make several of your own functions in this chapter. The first example will do one of the things that most of the applications in this book have done: prompt the user and then wait for them to print Enter or Return before continuing. Here, you will take that code and turn that into a function.

To define and use your own functions

  1. Create a new, blank text document in your text editor or IDE (Script 5.1).

    // prompt.cpp - Script 5.1
    #include <iostream>

  2. Add the function prototype.

    void promptAndWait();

    The new function being defined is called promptAndWait. It takes no arguments (the parentheses are empty) and returns no value (indicated by the void).

  3. Begin the main function.

    int main() {

    Even though this application has a user-defined function, it still requires a main() function. Remember that the main() function is automatically called when an application is executed, so the core of any application must still go here.

  4. Have the main() function do something.

    std::cout << "The main() function is doing something... ";

    Since the purpose of this application is to demonstrate the process of defining and using your own functions, it won’t do much else. If you’d like, have the main() function do something like convert some numbers from one unit to another, take user input, or whatever.

    Script 5.1. The user-defined promptAndWait() function takes some frequently used code and wraps it under a function name. The main() function calls promptAndWait() when needed.

    image

    image

  5. Call the user-defined function.

    promptAndWait();

    To call the function, simply type its name followed by empty parentheses. The line concludes with a semicolon, as does every statement in C++.

  6. Complete the main() function.

        return 0;
    } // End of the main() function.

    When your applications contain multiple functions, you may find it useful to comment on where each function terminates.

  7. After the main() function, begin defining the promptAndWait() function.

    void promptAndWait() {

    The definition begins exactly like the prototype. Instead of concluding with a semicolon, the definition itself then has a curly brace, which marks the start of the function’s contents.

  8. Add the function’s body.

    std::cout << "Press Enter or Return to continue. ";
    std::cin.get();

    The function prompts the user to press Enter or Return and then waits until that happens. If your main() function takes some input (which this program in the book does not), you’ll probably want to add

    std::cin.ignore(100, ' '),

    or

    std::cin.ignore(std::gcount()+1);

    as the first line in the function. This is in case a leftover Return or other character is sitting around (see the preceding chapter for more about the need for, or syntax of, this code).

  9. Complete the promptAndWait() function.

    } // End of promptAndWait() function.

    This closing brace marks the end of the function definition. We’ve added a comment so that it’s clear where the function begins and ends. Notice as well that there’s no return statement here, as this function returns nothing.

  10. Save the file as prompt.cpp, compile, and run the application (Figure 5.1).

    Figure 5.1. While the program doesn’t seem to do anything different or important when you run it, what’s going on behind the scenes—namely, the addition of a user-defined function—is what really counts.

    image

image Tips

• If a program makes use of the same function several times, or if you have a function or set of functions that you use in many applications, you’ll want to place these in their own library file. You’ll learn more about this in Chapter 12, “Namespaces and Modularization.”

• Programmers have many theories as to how you should name your functions, mostly because coming up with theories is what programmers do. The only critical rule is that you use a consistent naming scheme (promptAndWait or prompt_and_wait or PromptAndWait are all perfectly valid; just don’t use promptAndWait as one function name but do_this_and_that as another). Naturally, your function’s name should in itself indicate what that function does.

• Being able to define your own functions is also an important aspect of object-oriented programming. As you’ll discover in Chapter 7, “Introducing Objects,” the object data type can have its own functions, which are called methods.

Creating Functions That Take Arguments

The simple promptAndWait() function works just fine and is a decent use of the concept, but there is so much more you can do with user-defined functions. The next feature to incorporate is the ability for a function to take arguments. Arguments, in case it’s not clear, are values that can be passed to a function so that the function can use them. For example, you’ve used the getline() function with two arguments: the input source (e.g., cin) and a string variable to which keyed input should be assigned.

To have your functions take arguments, list the argument type and name within the function’s parentheses:

// Prototype:
void factorial (unsigned short num);
// Definition:
void factorial (unsigned short num) {
    // Function body.
    // Use num.
}

To have a function take multiple arguments, separate each argument from the next by a comma, indicating each type and name individually:

void exponent (int num, int power);

(This function already exists in C++ as pow(), see Chapter 3, “Operators and Control Structures”; it’s just a syntactical example here.)

When calling a function that takes arguments, you need to pass it the appropriate values. As you’ve already seen, there are many ways of doing this, from passing literal values to variables to the results of calculations. Each of these function calls would be valid:

int n = 2;
int x = 3;
exponent (n, x);
exponent (n, 5);
exponent (3, 4);

It’s important to remember that the values passed to the function must be of the right type and order. In other words, the first value passed to a function will be assigned to the function’s first argument variable, and the second to the second, and so on. There’s no way to pass a value to the second function argument without passing something to the first.

In our next example, a user-defined function will take two arguments: a temperature in degrees and a single character indicating whether the temperature is in degrees Celsius or Fahrenheit. The function will then make the proper conversion and print the results, as you’ve done several times before.

To define and use functions that take arguments

  1. Create a new, blank text document in your text editor or IDE (Script 5.2).

    // temperature.cpp - Script 5.2
    #include <iostream>

  2. Add the function prototypes.

    void promptAndWait();
    void convertTemperature(float tempIn, char typeIn);

    The new function being defined is called convertTemperature. It takes two arguments, the first being a float and the second being a char. The promptAndWait() function will be defined and used in this program, too.

    Script 5.2. The user-defined convertTemperature() function takes two arguments and prints out the result of the conversion.

    image

    image

    image

    image

    image

  3. Begin the main() function.

    int main() {
        float temperatureIn;
        char tempTypeIn;

    The main() function needs two variables for storing the user input.

  4. Prompt for and take the user input.

    std::cout << "Enter a temperature and indicate whether it is in degrees Fahrenheit or Celsius: [##.# C/F] ";
    std::cin >> temperatureIn >> tempTypeIn;

    In Chapter 4, “Input, Output, and Files,” you saw how to take input, using an example just like this. First cin will attempt to read in a float and assign this to temperatureIn. Then a single character will be read into tempTypeIn.

  5. Call the new function, if the input is of the right type.

    if  (
        (tempTypeIn == 'C') ||
        (tempTypeIn == 'c') ||
        (tempTypeIn == 'F') ||
        (tempTypeIn == 'f')
        ) {
        convertTemperature (temperatureIn, tempTypeIn);

    In this version of the program, a little validation is being used to make sure that the tempTypeIn variable has an acceptable value. You can certainly do more validation than this; see Chapter 4 for examples. The important thing to remember when using your own functions is that the data sent to them must still be validated somehow (unless the purpose of the function is validation).

  6. Complete the if conditional.

    } else {
        std::cout << "The calculation could not be made due to invalid input. ";
    }

    If tempTypeIn does not have one of the acceptable values, the convertTemperature() function is not called and the problem is reported (Figure 5.2). Going back to the examples in Chapter 4, you could alternatively repeat the prompt and retake the user input.

    Figure 5.2. Although we focus on data validation in Chapter 4, it’s an important aspect of every program that takes input.

    image

  7. Complete the main() function.

        promptAndWait();
        return 0;
    }

  8. After the main() function, define the promptAndWait() function.

    void promptAndWait() {
        std::cin.ignore(100, ' '),
        std::cout << "Press Enter or Return to continue. ";
        std::cin.get();
    }

    This code is slightly different than it was in prompt.cpp (Script 5.1) but will be used the same way. Because this program takes some input, the call to the ignore() function will help get rid of potential extraneous input.

  9. Begin defining the convertTemperature() function.

    void convertTemperature(float tempIn, char typeIn) {

    As the prototype also indicates, the function takes two arguments. The first is a float called tempIn. The value of temperatureIn, which is the first argument provided when this function is called from main(), will be assigned to tempIn here. The same goes for typeIn, which receives the value of tempTypeIn from main().

  10. Declare any necessary variables and constants.

    const unsigned short ADD_SUBTRACT = 32;
    const float RATIO = 5.0/9.0;
    float tempOut;
    char typeOut;

    The two constants are needed for the actual conversion, whereas the two variables are used in the output. Again, this is similar to how this conversion was written in previous programs.

  11. Make the proper conversions.

    switch (typeIn) {
        case 'C':
        case 'c':
            tempOut = (tempIn / RATIO) + ADD_SUBTRACT;
            typeOut = 'F';
            break;
        case 'F':
        case 'f':
            tempOut = (tempIn  - ADD_SUBTRACT) * RATIO;
            typeOut = 'C';
            break;
    }

    There’s nothing new here; the temperature is just calculated according to whether the input is in degrees Celsius or Fahrenheit.

  12. Print the results and complete the function.

        std::cout << tempIn << " degrees "
        << typeIn << " is equal to "
        << tempOut << " degrees "
        << typeOut << ". ";
    }

  13. Save the file as temperature.cpp, compile, and run the application (Figures 5.3 and 5.4).

    Figure 5.3. Converting from Fahrenheit to Celsius.

    image

    Figure 5.4. Converting from Celsius to Fahrenheit.

    image

image Tips

• Technically, in C++ you do not need to name the arguments in the function prototype, but it is a good idea.

• You’ll see and hear the terms parameters and arguments used synonymously. They can both be used to refer to values sent to, and received by, a function when it is called.

• If you come from a C background, you’ll recognize that C and C++ indicate function arguments differently. Take the definition

void functionName();

In C, this indicates an unspecified number and type of arguments. In C++, it means that the function takes no arguments. The equivalent of that in C is

void functionName (void);

You can also use this construct in C++, if you prefer to be more explicit.

• If your function takes several arguments of the same type, you cannot use the variable declaration shortcut of separating each by a comma:

int n1, n2; // Valid

This is invalid:

void functionName (int n1, n2);

This is valid:

void functionName (int n1, int n2);

Setting Default Argument Values

A second way in which you can modify your function definition is by establishing a default value for an argument. The benefit of a default value is that it makes the argument optional. To do this, assign the argument variable a value within the function prototype (but not the definition) using the assignment operator:

void fN (int n1, int n2 = 6);

With this function definition, either of these function calls is valid:

fN (1);
fN (5, 20);

If an argument with a default value is passed a value as in the second example, that value will be used. If no value is passed, the default is used. So in the first example, n2 will have a value of 6 within the function. In the second example, n2 will have a value of 20.

Your functions can have as many arguments with default values as you want, but there is one trick: every required argument must come before the optional arguments. As examples, these two definitions are fine:

void fN (int n1, int n2 = 6, int n3 = 4);
void fN (int n1 = 1, int n2 = 6);

But these are not:

void fN (int n1 = 1, int n2); // NO!
void fN (int n1, int n2 = 6, int n3); // NO!

In this next example, a currency conversion function will be defined that turns U.S. dollars into Euros and prints the results. The function will take two arguments: a conversion rate and the number of dollars being converted, with 1 as the default value.

To use default argument values

  1. Create a new, blank text document in your text editor or IDE (Script 5.3).

    // currency.cpp - Script 5.3
    #include <iostream>

  2. Add the two function prototypes.

    void promptAndWait();
    void dollarsToEuros(float rate, unsigned dollars = 1);

    The only significant difference between the convertTemperature() function defined in the previous program and this dollarsToEuros() function is the use of a default argument value. The dollars variable will have a default value of 1, unless a new value is passed to the function when it is called.

  3. Start the main() function.

    int main() {
        float conversionRate = 0.832339;
        unsigned dollarsIn;

    For sake of simplicity, the conversion rate is defined as a variable in the program, as opposed to being taken as user input. The unsigned integer dollarsIn will store the user-submitted dollar amount.

  4. Show the exchange rate by calling the function.

    dollarsToEuros(conversionRate);

    When we call the function while providing only one argument, the default value of dollars—which is 1—will be used. The effect of this will be a heading that shows the conversion rate (Figure 5.5).

    Script 5.3. The second argument in the dollarsToEuros() function has a default value of 1, making that argument optional.

    image

    image

    image

    image

    Figure 5.5. When the dollarsToEuros() function is called without the second argument, this first line is printed, using the default value of one dollar.

    image

  5. Prompt the user and take the input.

    std::cout << "Enter a US dollar amount (without the dollar sign, commas or a decimal): [####] ";
    std::cin >> dollarsIn;

    The intent of the program is to read in a dollar amount from the user and then convert this into Euros. Here the user is prompted and the dollars are read in (Figure 5.6).

    Figure 5.6. Requesting the amount of U.S. dollars to be converted.

    image

  6. Show the conversion by calling the function.

    dollarsToEuros(conversionRate, dollarsIn);

    The dollarsToEuros() function is called again, this time providing it with the user-submitted dollar amount. Thus, the conversion will use dollarsIn number of dollars, not the default of 1.

  7. Complete the main() function.

        promptAndWait();
        return 0;
    }

  8. Define the promptAndWait() function.

    void promptAndWait() {
        std::cin.ignore(100, ' '),
        std::cout << "Press Enter or Return to continue. ";
        std::cin.get();
    }

  9. Begin defining the dollarsToEuros() function.

    void dollarsToEuros(float rate, unsigned dollars) {

    Remember that you do not want to establish the default argument values here, as that syntax would be incorrect. You only set the default values in the function prototype.

  10. Adjust the formatting of the output.

    std::cout.setf(std::ios_base::fixed);
    std::cout.setf(std::ios_base::showpoint);
    std::cout.precision(2);

    Because the results of the calculation will be a float (obtained by multiplying the float rate times the integer dollars), you’ll want to format the output. This code was introduced in Chapter 2, “Simple Variables and Data Types.” It states that a fixed decimal point should be used, that it should always be shown, and that it should contain exactly two numbers after the decimal.

  11. Print the results of the calculation and complete the function.

        std::cout << " $" << dollars
        << " US = " << (rate * dollars)
        << " Euros. ";
    }

    This part of the function should be pretty obvious to you, mostly just a matter of multiplying the conversion rate by the number of dollars. Newlines are added at the beginning and end of the printed text so that it looks nicer in the output.

  12. Save the file as currency.cpp, compile, and run the application (Figure 5.7).

    Figure 5.7. The user-submitted amount of U.S. dollars will be converted into Euros and then printed.

    image

image Tips

• The ability to set default argument values is another addition in C++ that is not present in C.

• Some programmers prefer to also have the default value indicated in a function’s definition. To do so, comment out the value assignment:

void fN (int n1, int n2 /* = 6 */) {
     // Function body.
}

Creating Functions That Return a Value

There is one last thing that many C++ functions do that has not yet been discussed: they often (but not always) return some sort of value. The size() function used on strings returns the number of characters in a string, and the pow() function, defined within the cmath file, returns a number to a certain power. To have your own functions return a value, use the return statement, as you already have been doing in the main() function. You can return any single value from a function:

return 1;

The returned value can even be based on a variable:

int num = 6.25;
return num;

Besides using return in the function, both the function prototype and its definition must indicate what type of value will be returned. You do so by preceding the function name with a type identifier, as in

// Prototype:
float multiply (float x, float y);
// Definition:
float multiply (float x, float y) {
    return x * y;
}

You can assign the returned values to variables when the function is called, use them in calculations, or even use them as the argument to another function:

float num = multiply (2.6, 890.245);
float sum = num + multiply (0.32, 6874.1);
std::cout << multiply (0.5, 32.2);

To demonstrate this, the next example will take an integer as its lone argument and return its factorial value. In case you don’t remember from the previous factorial example, in Chapter 3, the factorial is represented in math as n! and is equal to every number from 1 to n multiplied together (so 4! is equal to 1 * 2 * 3 * 4).

Script 5.4. This application uses a separate function to calculate and return the factorial of a user-submitted number.

image

image

image

image

To define functions that return values

  1. Create a new, blank text document in your text editor or IDE (Script 5.4).

    // factorial.cpp - Script 5.4
    #include <iostream>

  2. Add the two function prototypes.

    void promptAndWait();
    unsigned long returnFactorial(unsigned short num);

    The new function, returnFactorial(), takes one argument, in the form of an unsigned short integer called num. This function returns a value, which will be an unsigned long integer. These two data types are appropriate, as the factorial of even a small number will quickly near the maximum size of a long integer.

  3. Start the main() function.

    int main() {
        unsigned short numberIn;

    The main part of the program requires one variable, which will store the user-submitted number. Its type—unsigned short—matches what will be sent to the returnFactorial() function.

  4. Prompt the user and assign the input to numberIn.

    std::cout << "Enter a small, positive integer: [##] ";
    std::cin >> numberIn;

    At this point you could also perform some validation, such as making sure that an unsigned integer between 1 and 12 was entered. The latter would be a good upper limit, as 13! eclipses the maximum long integer value on 32-bit computers.

    (If that last statement doesn’t make sense right now, you’ll see specifically how this plays out in Chapter 10, “Error Handling and Debugging.”)

  5. Print the factorial of the inputted number.

    std::cout << "The factorial of " << numberIn
    << " is " << returnFactorial (numberIn) << ". ";

    The inputted number is sent back to the output along with the calculated factorial (Figure 5.8). Since the returnFactorial() function returns a value, the function call can be placed within a cout statement, as it is here. The end result will be that the value returned by the function will be sent to the output stream.

    Figure 5.8. The inputted number and its factorial are displayed to the user.

    image

  6. Complete the main() function.

        promptAndWait();
        return 0;
    }

  7. Define the promptAndWait() function.

    void promptAndWait() {
        std::cin.ignore(100, ' '),
        std::cout << "Press Enter or Return to continue. ";
        std::cin.get();
    }

  8. Begin defining the returnFactorial() function.

    unsigned long returnFactorial (unsigned short num) {

    The definition begins exactly like its prototype.

  9. Calculate the factorial.

    unsigned long sum = 1;
    for (int i = 1; i <= num; ++i) {
        sum *= i;
    }

    The syntax for this calculation was introduced in Chapter 3. Every number between 1 and the submitted number has to be multiplied together. To do so, sum is initialized as 1 and then assigned the value of itself times i.

  10. Return the factorial and complete the function.

        return sum;
    }

    The sum variable has the value of the calculated factorial and can now be returned. Notice that the data type of sum—unsigned long integer—matches the return type for the function.

  11. Save the file as factorial.cpp, compile, and run the application (Figure 5.9).

    Figure 5.9. The function will calculate and return the factorial of any small, unsigned integer.

    image

image Tips

• You can have multiple return statements in a function, but only one return statement will ever be executed. A common example is something like

if (condition) {
    return 1;
} else {
    return 0;
}

• The return statement stops the execution of a function. Any code after an executed return will never be executed:

int myF () {
    return 1;
    std::cout << "Function returned 1";
}

The Function returned 1 message will never be printed as the std::cout line will not be executed.

• To be really formal, you can use

return;

in a function that returns no values.

Overloading Functions

Overloading functions in C++ is a slightly more advanced topic than what you’ve seen so far but is at the heart of the language’s flexibility and power. Overloading a function is the process of defining a function with the same name (and the same purpose) but with different arguments, either in number or in type. If you take a look at the convertTemperature() function (see Script 5.2), you may see why this would be necessary.

As it stands, the function takes one float argument and another char. But what if you wanted to convert an integer? You couldn’t do that with this version of the function because it accepts a float (the function call would still work, but the number 64 would be received as 64.0: a float, not an integer). You could create a different function, say convertIntegerTemperature(), but then your program would need to call one function in one circumstance and another function otherwise. A better solution is to just define the function again, but this time so that it can take an integer value.

The two prototypes would look like this:

void convertTemperature (float tempIn, char typeIn);
void convertTemperature (int tempIn, char typeIn);

Once you’ve defined an overloaded function, which definition gets used will be determined by the arguments in the function call.

convertTemperature (54.9, 'F'),
convertTemperature (16, 'C'),

Both functions would, in this case, have the same contents, but the right version would be called in each of these cases.

The final question, then, is when would you want to use this? For the most part, overloaded functions are used so that the same process can handle different data types. Thus one definition of myFunction() would accept integers, another would accept real numbers, and so on. This is demonstrated in the preceding example and in the following program. You’ll also see the concept, as well as the similar idea of overriding a function, when it comes to working with objects in Chapter 7, “Introducing Objects,” and Chapter 8, “Class Inheritance.”

Script 5.5. By adding another definition of the dollarsToEuros() function, the same function can be used to handle either real numbers or integers.

image

image

image

image

image

To overload a function

  1. Open currency.cpp (Script 5.3) in your text editor or IDE.
  2. Change the original prototype of dollarsToEuros() so that dollars no longer has a default value (Script 5.5).

    void dollarsToEuros(float rate, unsigned dollars);

    Relying upon default values with overloaded functions can cause ambiguity problems, so that part of the function will be removed.

  3. Add a second dollarsToEuros() prototype.

    void dollarsToEuros(float rate, float dollars);

    This prototype is exactly the same in every way except for the type of the second argument. Remember that function names are case sensitive, so the spelling and capitalization must exactly match.

  4. Within the main() function, declare another variable.

    float dollarsInFloat;

    To test the overloading concept, this program will first take integer input, and then a real number input. The latter will be stored in this variable.

    The next three steps will all require edits within the main() function as well.

  5. Remove the first call to dollarsToEuros().

    In the original version of this application, the function was called once to use the default dollar value. As it stands, if you did that with two function definitions (assuming both had a default argument value for dollars), you’d see an error, as the compiler wouldn’t know which function was being called (Figure 5.10).

    Figure 5.10. Since the second argument in the overloaded function differs from one definition to the next, trying to rely upon a default value for it confuses the compiler.

    image

  6. Prompt for and read in a floating-point dollar amount.

    std::cout << "Enter a US dollar amount (without the dollar sign or commas): [####.##] ";
    std::cin >> dollarsInFloat;

  7. Call the dollarsToEuros() function using the float as the second argument.

    dollarsToEuros(conversionRate, dollarsInFloat);

    Because dollarsInFloat is a float, this line will call the second definition of dollarsToEuros().

  8. After the existing function definitions, add the second dollarsToEuros() definition.

    void dollarsToEuros(float rate, float dollars) {
        std::cout.setf(std::ios_base::fixed);
        std::cout.setf(std::ios_base::showpoint);

        std::cout.precision(2);
        std::cout << " $" << dollars
        << " US = " << (rate * dollars)
        << " Euros. ";
    }

    The only difference here is that the second argument is of type float.

  9. Save the file as overload.cpp, compile, and run the application (Figure 5.11).

    Figure 5.11. The same function can now be called to handle either whole or real numbers. If an integer is entered, like 24, it’s used in one version of the function. With real numbers, the other version of the function is called.

    image

image Tips

• You could also modify both dollarsToEuros() functions so that they return the converted amount instead of printing it. Another alteration you could make is to ignore any existing input before reading in the dollars the second time. As it stands, if a user enters 300.75 for the first number, cin assigns just 300 (the integer) to dollarsIn, leaving .75 in the buffer which will be read in as the float (Figure 5.12).

Figure 5.12. The application could be improved by better validation and buffer management, eliminating unintended results like these.

image

• You cannot overload a function by just modifying the type of value returned, although an overloaded function may also differ in this way.

• Since the dollarsToEuros() function has only two arguments and it is the second argument that differs between the two definitions, you might want to switch the arguments around—put the dollars first, then the rate—so that it’s more obvious how they differ.

• To re-stress the point, normally you’ll use overloaded functions so that the same process can handle different data types. If you just need a function that may take, say, two arguments or three, then defining one function whose third argument is optional (because it has a default value) is the better way to go.

Understanding Variable Scope

Now that you have an understanding of writing and using your own functions, it’s time to revisit the topic of variables. This hasn’t been discussed before, but variables have a scope: a realm in which they exist. As you’ve seen, once you’ve declared a variable, you can refer to it, but where you can refer to that variable is dependent upon where it was defined. Here are some rules to go by:

• A variable defined within a code block is only available within that code block. A block is a group of instructions grouped together within curly braces, like a conditional, a loop, or even:

{
// This is a block.
}

• A variable defined within a function is only available within that function. These are called local variables.

• Variables defined outside of any function are accessible within any function defined thereafter. These are called global variables.

The first rule can be demonstrated with the following code:

for (int i = 1; i <= 10; ++ i) {
    std::cout << i << " ";
}
/* i no longer exists outside of the loop! */

Or, a less useful example:

if (1) {
    bool isTrue = true;
} // isTrue no longer exists!

Script 5.6. This simple program shows how different variables exist within specific realms, depending upon where they are defined.

image

image

image

image

With respect to the second rule, a global variable is defined outside of a function:

unsigned short age = 60;
int main() {...}
void sayHello() {...}

Both the main() and sayHello() functions can refer to age, as it has global scope.

The third rule really trips up the beginner programmer, so a demonstration will be written to illustrate the concept of variable scope. This demonstration will also make use of a global variable, so you can see how they differ from local ones.

To work with variable scope

  1. Create a new, blank text document in your text editor or IDE (Script 5.6).

    // scope.cpp - Script 5.6
    #include <iostream>
    #include <string>

    This program also includes the string header file, as it will use variables of that type.

  2. Add the two function prototypes.

    void promptAndWait();
    void nameChange();

    The new function, nameChange(), takes no arguments and returns no values. It will be used to highlight the differences between variables within main(),variables within other functions, and variables outside of functions.

  3. Create a global variable.

    std::string gName;

    This variable, defined outside of any function, will be available within any function defined after this point.

  4. Start the main() function.

    int main() {
        std::string name = "Andi";

    The variable name is local to the main() function. It can now be used anywhere within this function from this point onward (until the curly brace that closes the function).

  5. Assign a value to gName and print both variables.

    gName = name;
    std::cout << "At first, name is " << name << " and gName is " << gName << ". ";

    In the first line, the gName variable, which is global, is assigned the value of the name variable. So both variables now have the same value. This will be confirmed by printing both.

  6. Call the nameChange() function and then print the values of the variables again.

    nameChange();
    std::cout << "After the function, name is " << name << " and gName is " << gName << ". ";

    As you’ll soon see, the nameChange() function will assign values to two variables. To confirm how that affects the variables accessible within main(), another cout line is included after calling the function.

  7. Complete the main() function.

        promptAndWait();
        return 0;
    }

  8. Define the promptAndWait() function.

    void promptAndWait() {
        std::cout << "Press Enter or Return to continue. ";
        std::cin.get();
    }

    As with the first example in this chapter, the ignore() function has been omitted, as the application takes no input.

  9. Begin defining the nameChange() function.

    void nameChange() {
        std::string name = "Larry";

    This function has its own variable called name. Although this variable has the same identifier and is of the same type as the name variable in the main() function, these are two separate and unrelated variables.

  10. Print the current values of the variables.

    std::cout << "In the function, name is " << name << " and gName is initially " << gName << ". ";

  11. Change the value of gName and print both variables again.

    gName = name;
    std::cout << "In the function, name is " << name << " and gName is now " << gName << ". ";

    The first line assigns the value of name (which is Larry in this function) to the global variable gName. Both variables are then printed.

  12. Complete the function.

    }

  13. Save the file as scope.cpp, compile, and run the application (Figure 5.13).

    Figure 5.13. The gName variable, which is global in scope, can be altered within both functions, whereas name, which is local to each function, cannot be.

    image

image Tips

• Even when you use a variable in a function call, like this:

int myVar = 20;
fN(myVar);

you are not truly passing that variable to the second function but rather that variable’s value. In the preceding code, the myVar variable still doesn’t exist within the someFunction() function.

• For that matter, suppose you have

void fN(int myVar);
int main() {
    int myVar = 20;
    fN(myVar);
}void fN(int myVar) {
    // Do whatever.
}

The myVar defined within the main() function and used in calling fN() is still different than the myVar that is the argument in fN().

• You might think that since global variables exist everywhere, it would be best to use nothing but global variables. Although this makes it easier for you, the programmer, it’s poor form.

• Be extra careful not to give global and local variables the same name. If you do, the local variable will take precedence within that function or code block and the global variable will not be available at that point.

• One way to work around variable scope is to pass values to a function by reference instead of by value. This requires pointers and addresses, which you’ll learn about in Chapter 6, “Complex Data Types.”

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.190.211