Chapter 4. Using Functions

After completing this chapter, you will be able to:

  • Declare function prototypes.

  • Define function bodies.

  • Call functions.

  • Deal with local and global variable scope.

  • Define and use overloaded functions.

By now, you should be fairly comfortable with basic C++/CLI syntax. You’ve seen how to declare variables, write statements, use operators, and perform simple console output. However, as your programs begin to grow larger, you need to organize your code to cope with the growing complexity.

In this chapter, you’ll learn how to divide a C++/CLI application into functions. First, you’ll see how to declare function prototypes to introduce the functions to the compiler. Next, you’ll see how to define function bodies to carry out the required processing. For example, you might write a function to calculate the expected growth on an investment or to extract the user’s password from a logon screen. Finally, you’ll see how to call a function from elsewhere in your application.

Declaring function prototypes

A function prototype is a single-line statement that introduces the name of a function to the compiler. The prototype also indicates what types of parameters can be passed into the function and what type of value the function returns. The combination of information about a function’s name and parameters is called the function signature.

Declaring a simple function prototype

The following example shows a simple function prototype:

void DisplayWelcome();

In this example, the name of the function is DisplayWelcome. The parentheses are required to indicate that this is a function. The parentheses are empty in this example, which means that the function doesn’t take any parameters. The void keyword at the beginning of the function prototype indicates that the function doesn’t return a value; presumably, the function just displays a welcome message on the screen.

Note

Some programming languages differentiate between functions (which return a value) and subroutines (which do not return a value). For example, Microsoft Visual Basic .NET uses the Function keyword for functions and the Sub keyword for subroutines. C++ only has functions; use the void return type if the function doesn’t return a value. Also, notice the semicolon at the end of the function prototype. The semicolon is a statement terminator, and it marks the end of the function prototype. A function prototype doesn’t give you any indication as to what the function does; it just provides the function signature.

In this exercise, you will declare a simple function prototype in a C++/CLI application. The function does not take any parameters, and it does not return a value, either.

  1. Start Visual Studio 2012 and create a new CLR Console Application project named InvestmentPlanner.

    After the project is created, the source file appears in the editor window.

  2. At the top of the file, immediately below the using namespace System; line, add the following function prototype:

    void DisplayWelcome();

    This line is the function prototype you saw earlier. You place function prototypes near the top of the source file so that they are visible to the rest of the code in the file.

  3. On the Build menu, click Build Solution to build your application and check that there are no syntax errors.

    There’s no point in running the application yet because you haven’t implemented or called the DisplayWelcome function. You’ll do that later in this chapter.

Declaring parameters in a function prototype

Functions can take parameters to make them more generic. You must declare the data types for these parameters in the function prototype.

In this exercise, you will declare a function prototype that uses parameters.

  1. Continue working with the project you created in the previous exercise.

  2. Add the following function prototype immediately below the void DisplayWelcome() line:

    void DisplayProjectedValue(double amount, int years, double rate);

    This function prototype declares a function named DisplayProjectedValue. The function takes three parameters: a double, an int, and another double. The compiler uses this information to ensure that the function is always called with the correct number and types of parameters.

    Tip

    Parameter names are optional in the function prototype. Strictly speaking, you could omit the parameter names and just specify the parameter types. However, parameter names help to convey the meaning of the parameters, so it’s good practice to use them.

  3. Build your application to check the syntax.

Declaring the return type in a function prototype

As well as specifying input parameters for a function, you must also specify a return type for the function. As you saw earlier, the void return type indicates that the function does not return a value.

In this exercise, you will see how to specify a non-void return type for a function.

  1. Continue working with the project from the previous exercise.

  2. Add the following function prototype immediately below the void DisplayProjectedValue() line:

    double GetInvestmentAmount();

    This function prototype declares a function named GetInvestmentAmount. The function doesn’t take any parameters, but it returns a double.

  3. Add another function prototype as follows, immediately below the double GetInvestment Amount() line:

    int GetInvestmentPeriod(int min, int max);

    This example shows how to declare a function that takes parameters and returns a value. The GetInvestmentPeriod function takes two int parameters and returns an int.

    Note

    The parameter types and return type are independent of one another. The fact that the GetInvestmentPeriod parameters and return type are all ints is entirely coincidental. It’s quite easy to imagine a function whose parameter types and return type are different, as shown in this example:

    double CalculateAverageValue(int number1, int number2);
  4. Build your application.

Declaring default values for function parameters

When you declare a function prototype, you can specify default values for some or all of its parameters. Default values are useful for parameters that usually have the same value each time the function is called. Specifying a default value for a function parameter means that you can omit the parameter value when you call the function; the compiler will substitute the default value on your behalf.

In this exercise, you will define default parameters in one of the function prototypes you declared earlier.

  1. Continue working with the project from the previous exercise.

  2. Find the following function prototype:

    int GetInvestmentPeriod(int min, int max);
  3. Modify the function prototype as follows to define default parameter values:

    int GetInvestmentPeriod(int min=10, int max=25);

    This function prototype has two parameters named min and max. The parameters are followed by = (the equal sign) and then a default value. We have defined a default value of 10 for the min parameter and a default value of 25 for the max parameter. You’ll see how to call this function in the section Calling functions later in this chapter.

  4. Build your application.

Defining function bodies

In the previous section, you learned how to declare function prototypes. Recall that a function prototype specifies the name of a function, its parameter list, and its return type. However, function prototypes do not contain any executable statements; they do not inform you as to what the function will do when it is called.

To provide the behavior for a function, you must define a function body. The function body contains executable statements to perform the desired operations in the function. In this section, you will define function bodies for all the function prototypes introduced earlier.

Defining a simple function body

The following example shows a simple function body, corresponding to the DisplayWelcome function prototype from earlier in chapter:

void DisplayWelcome()
{
    Console::WriteLine("---------------------------------------");
    Console::WriteLine(
        "Welcome to your friendly Investment Planner");
    Console::WriteLine("---------------------------------------");
    return;
}

Notice that the first line of the function body is identical to the function prototype, except that there is no semicolon. This first line is known as the function header.

After the function header, a pair of braces ({}) encloses the executable statements for the function body. In this example, the DisplayWelcome function displays a simple welcome message on the screen. In the next two sections you’ll see more complex functions that perform console input and mathematical calculations.

The return keyword at the end of the function causes flow of control to return to the calling function. In this example, the return keyword is superfluous because the closing brace of the function acts as an implicit return. However, you can use return in other locations in a function, such as within an if statement, to return prematurely from a function. You’ll see more about the if statement in Chapter 5.

In this exercise, you will add the DisplayWelcome function body to your C++/CLI application.

  1. Continue working with the project you created earlier in this chapter.

  2. Locate the end of the main function. On the next line, define the DisplayWelcome function body as follows:

    void DisplayWelcome()
    {
        Console::WriteLine("--------------------------------");
        Console::WriteLine(
            "Welcome to your friendly Investment Planner");
        Console::WriteLine("---------------------------------");
        return;
    }
  3. Build your application. You shouldn’t get any compiler errors.

Note

You can define function bodies in any order in C++/CLI. For example, you can place the DisplayWelcome function body before or after the main function body. However, functions cannot be nested. You can’t define one function body inside the braces ({}) of another function.

Defining a function body that uses parameters

When you define a function body that uses parameters, you must define exactly the same number and types of parameters as in the function prototype. This is quite reasonable: The whole point of the function prototype is to introduce the exact signature of the function.

Tip

The function body can use different parameter names than the prototype because the parameter names in the prototype are there just for documentation. However, for consistency, you should use the same parameter names in the prototype and the function body.

In this exercise, you will define a function body for the DisplayProjectedValue function. You saw the prototype for this function earlier.

void DisplayProjectedValue(double amount, int years, double rate);

The function body will have the same signature as the prototype and will calculate the projected value of an investment after a specified number of years at a particular growth rate.

  1. Continue working with the project from the previous exercise.

  2. Scroll to the end of the source code and add the following lines—this is the start of the DisplayProjectedValue function body:

    void DisplayProjectedValue(double amount, int years, double rate)
    {
  3. Define some local variables within the function:

        double rateFraction = 1 + (rate/100);
        double finalAmount = amount * Math::Pow(rateFraction, years);
        finalAmount = Math::Round(finalAmount, 2);

    Here, the rateFraction variable holds the growth rate as a fractional value. For example, if the rate is 6 percent, rateFraction will be 1.06.

    The expression Math::Pow(rateFraction, years) shows how to raise a number to a power in C++/CLI. For example, Math::Pow(1.06, 3) is equivalent to 1.06 * 1.06 * 1.06.

    The expression Math::Round(finalAmount, 2) rounds finalAmount to two decimal places. For example, if finalAmount is 1000.775, the rounded value will be 1000.78.

  4. Add the following statements to the function to display the result of the calculations:

        Console::Write("Investment amount: ");
        Console::WriteLine(amount);
        Console::Write("Growth rate [%]: ");
        Console::WriteLine(rate);
        Console::Write("Period [years]: ");
        Console::WriteLine(years);
        Console::Write("Projected final value of investment: ");
        Console::WriteLine(finalAmount);
        return;
    }
  5. Build your application.

Defining a function body that returns a value

When you define a function with a non-void return type, you must return an appropriate value from the function. To return a value, use the return keyword followed by the value that you want to return.

Note

If you forget to return a value, you’ll get an error when the compiler reaches the closing brace of the function. This point is where the compiler realizes you haven’t returned a value from the function.

In this exercise, you will define a function body for the GetInvestmentAmount function. Here is the prototype for the function, as you saw earlier:

double GetInvestmentAmount();

The function asks the user how much money she wants to invest. It returns this value as a double data type.

You will also define a function body for the GetInvestmentPeriod function. The prototype for this function is as follows:

int GetInvestmentPeriod(int min=10, int max=25);

The function asks the user how long she wants to invest the money. It returns this value as an int value.

  1. Continue working with the project from the previous exercise.

  2. Scroll to the end of the source code and define the GetInvestmentAmount function body as follows:

    double GetInvestmentAmount()
    {
        Console::Write("How much money do you want to invest? ");
    
        String ^input = Console::ReadLine();
        double amount = Convert::ToDouble(input);
        return amount;
    }

    The first statement displays a prompt message on the console, asking the user how much money she wants to invest. The Console::ReadLine function call reads a line of text from the keyboard, and the result is assigned to a String variable.

    The Convert::ToDouble function call parses the String and converts it to a double value. The return statement returns this value back to the calling function.

    Tip

    You can declare local variables anywhere in a function. For example, here the input and amount variables are declared halfway down the GetInvestmentAmount function. Typically, you should declare variables at the point where they are first needed in the function, which is different from the C programming language, for which you have to declare local variables at the start of a block.

  3. Add the following function body:

    int GetInvestmentPeriod(int min, int max)
    {
        Console::Write("Over how many years [");
        Console::Write("min=");
        Console::Write(min);
        Console::Write(", max=");
        Console::Write(max);
        Console::Write("] ? ");
    
        String ^input = Console::ReadLine();
        int years = Convert::ToInt32(input);
        return years;
    }

    The Console::Write function calls ask the user to enter a value between min and max. These values are supplied as parameters into the GetInvestmentPeriod function.

    The Console::ReadLine function call reads the user’s input as a String, and the Convert::ToInt32 function call converts this value into a 32-bit integer. The return statement returns this value to the calling function.

    Note

    The function prototype for GetInvestmentPeriod declared default values for the min and max parameters. The default value for min is 10, and the default value for max is 25. Default values are specified only in the function prototype—you don’t mention these default values in the function body. If you accidentally define the default values in the function body as well as in the function prototype, you’ll get a compiler error at the function body.

  4. Build your application.

Calling functions

Now that you have defined all the function bodies in the sample application, the last step is to call the functions at the appropriate place in the application.

To call a function, specify its name followed by a pair of parentheses. For example, you can call the DisplayWelcome function as follows:

DisplayWelcome();

This is a simple example because the function doesn’t take any parameters or return a value.

If you want to call a function that returns a value, you can assign the return value to a variable. The following example calls the GetInvestmentAmount function and assigns the return value (a double) to a local variable named sum:

double sum = GetInvestmentAmount();

Note

You can ignore the return value from a function if you want. When you call the function, leave out the assignment operator on the left side of the function name. The function still returns the value, but the value is discarded.

If you want to call a function that takes parameters, pass the parameter values between the parentheses in the function call. The following example calls the DisplayProjectedValue function, passing in three literal values as parameters:

DisplayProjectedValue(10000, 25, 6.0);

Note

You don’t specify the parameter data types when you call a function. Just provide the parameter values.

The following example shows how to call a function that takes parameters and returns a value. In this example, you call the GetInvestmentPeriod function to get a value between 5 and 25. You assign the return value to a local int variable named period:

int period = GetInvestmentPeriod(5, 25);

Calling functions in the sample application

In this exercise, you will extend your sample application to include the function calls you’ve just seen.

  1. Continue working with the project from the previous exercise.

  2. Locate the main function and then replace the line that prints “Hello, world” with the following statement, which calls the DisplayWelcome function:

        DisplayWelcome();
  3. Add the following statements to display an illustration of investment growth.

    Console::WriteLine("
    Illustration...");
    DisplayProjectedValue(10000, 25, 6.0);

    The DisplayProjectedValue function call displays the value of 10,000 after 25 years at a growth rate of 6 percent.

  4. Next add the following statements to ask the user how much he wants to invest and for how long.

    Console::WriteLine("
    Enter details for your investment:");
    double sum = GetInvestmentAmount();
    int period = GetInvestmentPeriod(5, 25);

    The GetInvestmentAmount and GetInvestmentPeriod function calls return these values.

    Note

    The GetInvestmentPeriod function has default values for each of its parameters. (The first parameter has a default value of 10, and the second parameter has a default value of 25.) You can use these default values when you call the function. For example, the following function call uses the default value for the second parameter:

    int period = GetInvestmentPeriod(5);   // First parameter is 5;
                                           // second parameter
                                           // defaults to 25.

    If you use a default value for a parameter, you must use the default values for each subsequent parameter in the parameter list. For example, the following function call is invalid:

    int period = GetInvestmentPeriod(, 20);  // Try to use default value
                                             // for just the first
                                             // parameter - illegal.
  5. Add the following statements to calculate and display the projected final value of this investment, assuming a growth rate of 6 percent:

    Console::WriteLine("
    Your plan...");
    DisplayProjectedValue(sum, period, 6.0);
  6. Build your application and fix any compiler errors. On the Debug menu, click Start Without Debugging to run the application. You should see output similar to the following:

    A screenshot of the console output showing, the output up until the point where the user enters the amount he wants to invest.

Stepping through the application by using debugger

In this exercise, you will step through the application by using the debugger. Doing so will help you understand how the flow of control passes from one function to another in your application. This exercise also illustrates the concept of variable scope. You will see how local variables in a function come into scope during the function’s execution and disappear from scope at the end of the function.

  1. Open the project from the previous exercise.

  2. Locate the main function.

  3. In the gray border to the left of the code, click next to the DisplayWelcome function call to insert a debug breakpoint. A red dot appears in the border, as shown in the graphic that follows.

    Tip

    If you add a breakpoint in the wrong place, simply click again on the red dot to remove it.

    A screenshot of the editor window. A red dot is visible in the left margin to indicate a breakpoint.
  4. Start the debugging session by pressing F5.

    After the application loads, it executes and stops at the breakpoint in the main function.

    A screenshot of the editor window after a debugger session has been started. A right-facing yellow arrow appears over the red dot in the margin.

    A yellow arrow appears in the margin next to the DisplayWelcome function call. The yellow arrow indicates that this is the next statement to be executed.

  5. Press F11 to step into the DisplayWelcome function.

    The debugger calls the DisplayWelcome function and displays a yellow arrow at the start of that function.

    The same screenshot of the editor window as in the last graphic, but this time, the yellow arrow in the left margin shows that the next statement to be executed will be the first statement in the DisplayWelcome function.

    Note

    You can also use the Debug toolbar to control the debugger. To display the Debug toolbar, on the View menu, point to Toolbars and then click Debug from the list of toolbars that appears. Each of the debug function keys mentioned in the remainder of this exercise has an equivalent Debug toolbar button.

  6. Press F10 several times to step over each statement one at a time in the DisplayWelcome function.

    This causes a welcome message to be displayed in the console window. At the end of the function, the debugger returns you to the main function. The yellow arrow indicates the next statement to execute in main.

    The same screenshot of the editor window as in the last graphic. In this screenshot, both the yellow arrow and the red dot appear separately in the left margin. The breakpoint is still set on the first statement in main, but the yellow arrow has moved down to show the next statement to be executed.
  7. Press F10 to step over the Console::WriteLine function.

    The debugger executes the Console::WriteLine function but doesn’t take you through it step by step. The yellow arrow moves on to the DisplayProjectedValue function call in main.

  8. Press F11 to step into the DisplayProjectedValue function. On the Debug menu, point to Windows, and then click Locals.

    The local variables in this function appear.

    A screenshot of the Locals window, which displays underneath the editor window by default. The local variables that are currently in scope appear in the window.

    The Locals window displays five local variables. The first three variables—amount, years, and rate—are the function parameters. These variables are already initialized with the values you passed into the function.

    The last two variables—finalAmount and rateFraction—do not have meaningful values because the variables haven’t been assigned a value yet. In fact, the debugger is a little misleading here because the finalAmount and rateFraction variables haven’t even been declared yet. These variables don’t really exist until the variable declaration statements further on in the function.

  9. Press F10 several times to step over the statements in the DisplayProjectedValue function. Observe how the finalAmount and rateFraction variables change during the function. (The debugger displays values that were changed during the execution of the previous statement in red for prominence.) Take a look at the console window to see what is displayed.

  10. Keep pressing F10 until you reach the end of the DisplayProjectedValue function and return to main.

  11. In main, press F10 to step over the Console::WriteLine statement.

  12. Press F11 to step into the GetInvestmentAmount function. Step through the statements in this function. When the debugger executes the ReadLine statement, the console window appears and you are asked to enter a number. Type a number such as 20 and then press Enter.

  13. Keep stepping through the GetInvestmentAmount function until you return to main.

  14. Press F10 one more time and then examine the local variables in main. Notice that the return value from GetInvestmentAmount has been assigned to the sum local variable in main.

    A screenshot of the Locals window. Values of local variables that have changed during the execution of the previous statement appear in red.
  15. Continue stepping through the application in this manner until the application terminates.

Tip

If the debugger takes you into a function that you’re not interested in stepping through, press Shift+F11 to step out of the function. If you just want to run the application without stopping at all, press F5.

Understanding local and global scope

The previous exercise demonstrated how each function defines its own scope for local variables. The local variables are created during function execution and are automatically destroyed at the end of the function, which means you can quite happily have variables with the same name in different functions without interference.

It’s also possible to declare variables globally, outside of any function. Global variables are visible in all function bodies that come after the global variable definition in your source file. You can use global variables as a rudimentary way of sharing information between multiple functions.

Important

Global variables are generally considered bad programming practice, especially in object-oriented languages such as C++. Global variables have too much visibility. Because global variables can often be used in several functions, if one becomes corrupt, it can be difficult to pinpoint where the problem occurred. Global variables also introduce too much dependency between functions.

For these reasons, you should use global variables sparingly. A better way of sharing information between functions is to pass parameters and return values, as you saw earlier in this chapter.

In this exercise, you will define a global variable in your application. You will use this global variable in several functions to illustrate its global scope.

  1. Continue working with the project from the previous exercise.

  2. Before the start of the main function, define a global integer variable named numberOfYourFunctionsCalled, as follows:

    int numberOfYourFunctionsCalled = 0;
  3. Find the DisplayWelcome function in your code. At the start of this function, increment the numberOfYourFunctionsCalled variable, as shown in the following.

    A screenshot of the editor window, showing a line incrementing the numberOfYourFunctionsCalled global variable as the first line of a function. Doing this in each function will enable you to count the number of functions you have called.

    Note

    You can click the minus sign (–) symbol to the left of the code to collapse a block of code. To view a collapsed block, click the plus sign (+) to expand it again. This can make it easier to work with code by hiding functions that are not of interest at the moment. In the preceding screen shot, the main function has been collapsed.

  4. Add a similar statement to the start of every function in your application.

  5. Modify the main function. At the end of this function, just before the return statement, display the value of the numberOfYourFunctionsCalled variable.

    A screenshot of the editor window, showing the lines that write out the numberOfYourFunctionsCalled variable at the end of the main function. Doing this will show you how many functions you have called.
  6. Build and run your application. How many of your functions are called during the application?

Overloading functions

With C++/CLI, you can provide many functions with the same name, as long as each function has a different parameter list. This process is known as function overloading. Function overloading is useful if you have several different ways of performing a particular operation based on different input parameters.

For example, you might want to provide an Average function to find the average value of two double values, and you might have another Average function to find the average value of an array of integers. You can define two functions to support these requirements. Give each function the same name, Average, to emphasize the common purpose of these functions. Define different parameter lists for the functions to differentiate one from another.

double Average(double number1, double number2);
double Average(int array[], int arraySize);

You must still implement both of these functions—there is no magic here! When you call the Average function, the compiler deduces which version of the function to call based on the parameter values you supply.

Note

If you define overloaded functions, the functions must have different parameter lists. If you define overloaded functions that differ only in their return type, you’ll get a compiler error.

In this exercise, you will define an overloaded version of the DisplayProjectedValue function. The new version calculates a random growth rate between 0 and 20 percent rather than use a specific growth rate.

  1. Continue working with the project from the previous exercise.

  2. Add the following function prototype at the start of your code, below the existing prototype for DisplayProjectedValue:

    void DisplayProjectedValue(double amount, int years);
  3. In the main function, locate the second call to the DisplayProjectedValue function. Modify the function call so that you pass only two parameters into the function.

    DisplayProjectedValue(sum, period);
  4. Define the new DisplayProjectedValue function body as follows, placing it after the existing DisplayProjectedValue function:

    void DisplayProjectedValue(double amount, int years)
    {
        numberOfYourFunctionsCalled++;
    
        Random r;
        int randomRate = r.Next(0, 20);
        DisplayProjectedValue(amount, years, randomRate);
    }

    Tip

    You now have two overloaded DisplayProjectedValue functions. It is good practice to keep overloaded functions together in the source code.

    This function uses the Random class to calculate a random number between 0 and 20. The function passes the random number into the original version of the DisplayProjectedValue function to calculate the value of the investment using this random rate.

  5. Define breakpoints at the start of both of the DisplayProjectedValue functions.

  6. Build the application and start it in the debugger.

  7. Observe which versions of DisplayProjectedValue are called as your application executes. See what random number the application uses for your growth rate.

  8. Run the application several times to verify that the growth rate really is random.

Quick reference

To

Do this

Declare a function prototype.

Specify the return type of the function, followed by the function name, followed by the parameter list enclosed in parentheses. Remember to include the semicolon at the end of the function prototype. For example:

double MyFunction(int p1, short p2);

Define default parameters.

Define default parameters in the function prototype, if required. Use an = operator, followed by the default value. For example:

double MyFunction(int p1, short p2=100);

Define a function body.

Specify the return type of the function, followed by the function name, followed by the parameter list enclosed in parentheses. Do not specify default parameters here. Define the function body within braces. For example:

double MyFunction(int p1, short p2)
{
  int n = p1 + p2;
  ...
}

Return a value from a function.

Use the return keyword, followed by the value that you want to return. For example:

return (p1 + p2) / 2.00;

Call a function.

Specify the function name and pass parameter values within parentheses. If the function returns a value, you can assign it to a variable. For example:

double result = MyFunction(100, 175);

Define and use global variables.

Define the global variable outside of any function. Use the variable in any subsequent function in the source file. For example:

int myGlobal = 0;

void MyFunction()
{
  myGlobal++;
  ...
}

Define and use overloaded functions.

Define several functions with the same name but different parameter lists. Implement each function. Call the version you want, using appropriate parameter values. For example:

// Prototypes
void MyFunction(int p1);
void MyFunction(double p1, double p2);
...
// Function calls
MyFunction(100);
MyFunction(2.5, 7.5);
...
// Function bodies
void MyFunction(int p1)
{
  ...
}
void MyFunction(double p1, double p2)
{
  ...
}
..................Content has been hidden....................

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