6: More Functionality

What We Will Cover


Continuations

Homework Questions?

Questions from last class?

What is output by the following program? (Do not run the code -- work it out by hand)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int mystery(int param);

int main() {
    int num = 2;
    cout << "At first, num=" << num << endl;
    num = mystery(num);
    cout << "Later, num=" << num << endl;
    return 0;
}

int mystery(int param) {
    cout << "param=" << param << endl;
    param = param * 2;
    return param;
}
  1. At first, num=2
    Later, num=2
    
  2. At first, num=2
    param=2
    Later, num=2
    
  3. At first, num=2
    param=2
    Later, num=4
    
  4. None of these

6.1: More Control Statements

Objectives

At the end of the lesson the student will be able to:

  • Use switch statements for branching
  • Use do-while statements to control loops
  • Use for statements to control loops
  • Use break and continue statements with loops
  • In this section, we learn about the rest of C++'s control statements
  • These statements do not add new capabilities
  • Just provide alternate ways of performing some tasks
  • Since C++ code has them, you need to know how to use them

6.1.1: switch Statements

  • The switch statement provides an alternative to an if-else chain
  • Executes a section of code depending on value of a variable
  • General form:
  • switch (integerExpression) {
       case label1:
          statements
          break;
       case label2:
          statements
          break;
       ...
       case labeln:
          statements
          break;
       default:
          statements
          break;
    }
    
  • Keyword switch identifies the start of the switch statement
  • Expression in parenthesis is evaluated for comparison to each case label
    • integerExpression must evaluate to an integer
    • Otherwise a compilation error occurs
  • Within a switch statement, keyword case labels each entry point into the code
    • Code executes if integerExpression matches the case label value
    • Execution starts with the statement immediately following the match
  • Any number of case labels can be placed in any order
  • Any value that does not match starts executing with the statement after default
  • Execution continues until the end of the switch statement or a break statement
  • The break statement causes an immediate exit from the switch statement
  • Just as case identifies possible starting points, break determines end points

For Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
using namespace std;

const int PARCEL_POST = 0;
const int UPS = 1;
const int FED_EX = 2;

int main() {
    int shippingOption = UPS;
    cout << "Enter a shipping option: ";
    cin >> shippingOption;

    switch (shippingOption) {
        case PARCEL_POST:
            cout << "Delivery in two weeks.\n";
            break;
        case UPS:
            cout << "Delivery in three days.\n";
            break;
        case FED_EX:
            cout << "Overnight delivery.\n";
            break;
        default:
          cout << "It may never get there!\n";
    }
    cout << "Ship it!\n";

    return 0;
}

  • Notice that each case other than the last contains a break statement
  • Ensures that the switch statement is exited after a matching case is found

When to Use switch Statements

  • You can only use switch statements with integer expressions
  • Inherently less useful than if-else statements
  • Syntax is no clearer than if-else statements
  • Thus, no real need to ever use a switch statement

6.1.2: Conditional (Ternary) Operators

  • Provides a compact if-else structure
  • Syntax:
  • operand1 ? operand2 : operand3;
    
  • First operand must be a conditional expression that evaluates to true or false
  • If operand1 is true, return operand2; otherwise return operand3.

For Example

  • Following is an if-else statement
  • string outMessage;
    int celsius;
    cout << "Enter the Celsius temperature: ";
    cin >> celsius;
    
    if (celsius >= 100) {
        outMessage = "Boiling!";
    } else {
        outMessage = "Not boiling";
    }
    
    cout << outMessage << endl;
    
  • Equivalent using the conditional operator
  • string outMessage;
    int celsius;
    cout << "Enter the Celsius temperature: ";
    cin >> celsius;
    
    outMessage = (celsius >= 100)
        ?  "Boiling!"
        : "Not boiling";
    
    cout << outMessage << endl;
    

When to Use Conditional (Ternary) Operators

  • Rarely used in professional programing
  • Almost always clearer to use if-else statements

6.1.3: do-while Statements

  • Variation of the while loop
  • Syntax:
  • //Initialization
    ...
    do {
       //loop body
       ...
    } while (loopTest); //loop condition
    
  • Initialization code may precede loop body
  • Loop test is after the loop body
  • Loop must execute at least once (minimum of at least one iteration)
  • Use when you want to force a minimum of one iteration

Validity Checks

  • You can use do-while statements to filter user input
  • For example, assume we need a positive value from the user:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int main() {
    //Loop initialization
    double input = 0.0;
    do {
        //loop body
        cout << "Enter a positive number: ";
        cin >> input;
        if (input < 0.0) {
            cout << "You must enter a positive number!\n";
        }
    } while (input < 0.0); //loop condition
    cout << "You entered: " << input << endl;

    return 0;
}

When to Use do-while Statements

  • Sometimes used in professional programming
  • You can accomplish the same control flow with a while loop
  • You can use it whenever you want to force a minimum of one iteration

6.1.4: for Statements

  • Provides a more compact loop
  • Syntax:
  • for (initializer; condition; increment) {
       //loop body
       ...
    }
    
  • Initialization, loop-test, and increment-expression are part of the syntax
  • initializer expression: defines the initial value used to control the loop
  • condition expression: determines when the loop will end
  • increment expression: evaluated at the end of each loop iteration
  • Statements to repeat are enclosed in curly brackets
  • Execution sequence as follows:
  1. When for statement reached -- initialization executes
  2. Check if condition is true
    • if true then continue with Step 3
    • Otherwise, continue with Step 6
  3. Execute block containing the loop body
  4. When end of loop body is reached, execute the increment
  5. Return to Step 2
  6. Loop is finished: continue with statements after the loop

Counter-Controlled-Loop Example

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using namespace std;

int main() {
    int count;
    for (count = 0; count < 5; count++) {
        cout << count << endl;
    }

    return 0;
}
  • Note that code is indented in the loop body for readability

Counting Down

  • Can also count downwards
  • Positive numbers step higher (+inf) and negative numbers step lower (-inf)
  • What is displayed with the following?
  • #include <iostream>
    using namespace std;
    
    int main() {
        int count;
        for (count = 5; count > 0 ; count--) {
            cout << count << endl;
        }
    
        return 0;
    }
    

When to Use for Statements

  • Very often used in professional programming
  • Especially useful for counter-controlled loops

6.1.5: break and continue Statements

  • break and continue statements transfer control to another part of a program
  • Within a loop break statements an immediate exit
  • Transfers control to the statement following the loop
  • Provides a way to break out of an intentional infinite loop
  • #include <iostream>
    using namespace std;
    
    int main() {
        int count = 0;
        while (true) {
            count++;
            if (count == 3) {
                continue;
            }
            cout << count << endl;
            if (count >= 5) {
                break;
            }
        }
        cout << "After count= " << count << endl;
    }
    
  • continue statement transfers control to the loop test
  • Causes the loop to skip the rest of the current iteration
  • When executed within a loop, starts the next iteration of the loop immediately

When to Use break and continue Statements

  • Use break statements in switch statements only
  • Never use break in looping constructs
    • Poor coding habit
    • Should only have one exit point in a loop
  • In this course, you will never need to write infinite loops
  • You do not need to use continue statements either
  • You can accomplish the same operation other ways

6.1.6: Summary

  • C++ provides many control statements
  • They provide alternatives to the basic statements covered earlier
  • Of those covered today, the most useful is the for loop
  • Provides a compact looping structure often used for counter-controlled repetition
  • Incudes three of the loop requirements in the syntax of the loop

Check Yourself

  1. What is the difference between a while and a do-while loop?
  2. What are the advantages of using a for loop compared to other looping statements?

Exercise 6.1

  1. Start a text file named exercise6.txt.
  2. Prepare the exercise header as described in the HowTo on submitting exercises
  3. Label this exercise: Exercise 6.1
  4. Complete the following and record the answers to any questions in exercise6.txt.

Specifications

  1. Save the following code as for5.cpp
  2. Convert the code to use a for loop rather than a while loop.
  3. Turn in the modified code along with your exercise6.txt file
  4. Record answers to the following questions in exercise6.txt.
  5. Q1: What is the chief advantage of using a for loop?

    Q2: The for loop seems most appropriate for which type of looping pattern?

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

int main() {
    int count = 1; // initialization code
    while (count < 6) { //loop termination condition
        //loop body
        cout << count << endl;
        count = count + 1; // adjust the counter
    }

    return 0;
}

6.2: Overloading Function Names

Objectives

At the end of the lesson the student will be able to:

  • Code overloaded functions
  • Describe how C++ resolves function calls
  • Describe when to overload functions

6.2.1: About Overloading

  • You can have several functions with the same name in a program
  • Need different parameter set (i.e. signature) for each function
    • Number of parameters
    • Parameter types
    • Order of parameters
  • Return type is not part of the signature
    • Cannot be used to distinguish between two functions with the same name and signature

For Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;

// Function prototypes
double square(double x);
int square(int x);

int main() {
    cout << square(5) << endl;
    cout << square(6.5) << endl;
}

int square(int x) {
    cout << "Using type int: ";
    return x * x;
}

double square(double x) {
    cout << "Using type double: ";
    return x * x;
}
  • First call to square uses function with int signature
  • Second call uses function with double signature
  • Using type double: 42.25
    Using type int: 25
    

6.2.2: Overloading Resolution

  • Function call resolution:
    1. Looks for exact signature
    2. Looks for 'compatible' signature
  • First: looks for exact signature
    • Because no argument conversion required
  • Second: looks for 'compatible' signature
    • First where promotion is possible, with no loss of data
    • int => double
    • Second with demotion, with a possible loss of data
    • double => int

For Example

  • Given following functions:
    1. void f(int n, double m) {}
    2. void f(double n, int m) {}
    3. void f(int n, int m) {}
  • Making these calls:
  • f(98, 99); => Calls 3
  • f(5.3, 4); => Calls 2
  • f(4.3, 5.2); => ??
    • Avoid such confusing overloading

6.2.3: Automatic Type Conversion and Overloading

  • Typically you should make numeric formal parameters of type double
  • Allows for any numeric type
  • Any narrower data type can safely be promoted to wider types
  • Avoids overloading because of different numeric types

For Example

    double mpg(double miles, double gallons) {
        return (miles / gallons);
    }
    
  • Example function calls:
  • mpgComputed = mpg(5, 20);
    • Converts 5 and 20 to doubles, then passes them to the function
  • mpgComputed = mpg(5.8, 20.2);
    • No conversion necessary
  • mpgComputed = mpg(5, 2.4);
    • Converts 5 to 5.0, then passes the values to the function

6.2.4: Summary

  • A programmer can declare many functions using the same name
    • However, the parameter list must be different between each function
  • Useful when you want to have the same function operate on different data types
  • However, you can avoid overloading for numbers by relying on automatic type conversion
    • C++ automatically changes values to the needed type
    • By making your numeric types double, you can use promotion instead of overloading

Check Yourself

  1. How many functions can you define for each program?
  2. If two functions have the same name, what must be different in each declaration and definition?
  3. How does C++ resolve overloaded functions?
  4. When should you overload functions?

Exercise 6.2

  1. Label this exercise: Exercise 6.2
  2. Complete the following and record the answers to any questions in exercise6.txt.

Specifications

  1. Suppose you have two functions:
  2. double score(double time, double distance) {}
    double score(double points) {}
    

    Which function would be used in the following function call?

    double finalScore = score(95.7);
    
  3. Suppose you have two functions:
  4. double answer(double data1, double data2) {}
    double answer(double time, int count) {}
    

    Which function would be used in the following function call?

    double y = 5.0;
    double x = answer(y, 6.0);
    

6.3: void Functions and Tracing Calls

Objectives

At the end of the lesson the student will be able to:

  • Describe how void functions differ from functions that return a value
  • Declare and define void functions
  • Decide when to use void functions
  • Trace function calls

6.3.1: void Functions

  • Previously we looked at functions that returned one value
  • Functions returning a value use the return statement
  • return result;
  • A function that returns no value is called a void function
  • In C++, void functions are defined like functions that return a value
  • However, the keyword void replaces the return type
  • For example, what do you notice that is different about the following?
  • void showResults(double fDegrees, double cDegrees) {
        cout << fDegrees
             << " degrees Fahrenheit is equivalent to "
             << cDegrees << " degrees Celsius." << endl;
        return;
    }
    
  • There are only two differences between definitions for void functions and other functions.
    • void return type
    • return statement is optional and does not specify a value if used
  • If no return type is specified, the function returns after executing the last statement
  • Why might you code a return statement if its use is optional?
  • Here is an example program using the void function shown above
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

// Displays the results of a ddegree conversion
void showResults(double fDegrees, double cDegrees);

int main() {
    double fTemperature, cTemperature;

    cout << "Enter a temperature in Fahrenheit: ";
    cin >> fTemperature;
    cTemperature = 5.0 / 9 * (fTemperature - 32);
    showResults(fTemperature, cTemperature);

    return 0;
}

void showResults(double fDegrees, double cDegrees) {
    cout << fDegrees
         << " degrees Fahrenheit is equivalent to "
         << cDegrees << " degrees Celsius." << endl;
    return;
}

6.3.2: Calling void Functions

  • When you call a void function you must code the call as a separate statement
  • A void function cannot be part of an expression
  • For example, look at the following function call:
  • showResults(32.5, 0.3);
  • This will cause the following to appear on the screen:
  • 32.5 degrees Fahrenheit is equivalent to
    0.3 degrees Celsius.
    
  • Note that the void function call does not return a value
  • If the function call was part of an expression like the following, what would be printed?
  • cout << showResults(32.5, 0.3); // not allowed
    
  • If you tried to compile code like this, you would get a lengthy set of error messages

6.3.3: Functions Calling Functions

  • Functions may call other functions
  • Within the body of the function, you can code a function call
  • We are already doing this when main() calls another function
  • What does the following program display to the console?

Example of Functions Calling Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <iomanip>
using namespace std;

// Returns the square of a number
double square(double number);

// Displays a message to the console
void out(string funName, double value);

int main() {
    double number = 5;
    out("main", number);
    double result = square(5);
    out("main", result);

    return 0;
}

double square(double number) {
    out("square", number);
    double result = number * number;
    out("square", result);
    return result;
}

void out(string funName, double value) {
    cout << "In " << funName << "() the value is "
         << value << endl;
}

6.3.4: Tracing a Function Call

  • When functions call functions, you need to follow the flow of execution
    • Often line by line
  • A function call transfers control to the first statement of the called function
  • When a return point is reached, control passes back to the point right after the function call
  • If the function call is part of an expression, the returned value is used in the expression
  • Let's trace the flow in the following code by line number:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <iomanip>
using namespace std;

// Returns the square of a number
double square(double number);

// Displays a message to the console
void out(string funName, double value);

int main() {
    double number = 5;
    out("main", number);
    double result = square(5);
    out("main", result);

    return 0;
}

double square(double number) {
    out("square", number);
    double result = number * number;
    out("square", result);
    return result;
}

void out(string funName, double value) {
    cout << "In " << funName << "() the value is "
         << value << endl;
}

6.3.5: Summary

  • You code a void return type when you do not want a function to return a value
  • For example:
  • void showResults(double fDegrees, double cDegrees) {
        cout << fDegrees
             << " degrees Fahrenheit is equivalent to "
             << cDegrees << " degrees Celsius." << endl;
        return;
    }
    
  • The return statement is optional for void functions
  • When a void function executes the last statement, it automatically returns
  • If a return statement is coded for a void function, it does not include a return value
  • return;
  • Functions with a void return type cannot be called as part of an expression
  • showResults(32.5, 0.3);
    NOT:
    cout << showResults(32.5, 0.3);
    
  • Functions can call other functions, just like main() calls other functions

Check Yourself

  1. How do void functions differ from functions that return a value?
  2. How do you declare and define a void function?
  3. When do you use a void function?
  4. Are you required to have a return statement in a void function definition?
  5. What effect would removing the return statement from the following function have on compiling or executing the program?
  6. void showResults(double fDegrees, double cDegrees) {
        cout << fDegrees
             << " degrees Fahrenheit is equivalent to "
             << cDegrees << " degrees Celsius." << endl;
        return;
    }
    
  7. Assuming that showResults() is a void function, what is wrong with:
  8. cout << showResults(32.5, 0.3); // not allowed
    
  9. How do you code a function to call another function?

Exercise 6.3

Specifications

  1. Label this exercise: Exercise 6.3
  2. In exercise6.txt, list the line numbers of the following program in the order they are processed.
  3. Do not bother to list lines containing just a closing curly brace (}) of a function definition.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <iomanip>
using namespace std;

// Returns the square of a number
double square(double number);

// Displays a message to the console
void out(string funName, double value);

int main() {
    double number = 5;
    out("main", number);
    double result = square(5);
    out("main", result);

    return 0;
}

double square(double number) {
    out("square", number);
    double result = number * number;
    out("square", result);
    return result;
}

void out(string funName, double value) {
    cout << "In " << funName << "() the value is "
         << value << endl;
}

6.4: Call-By-Reference Parameters

Objectives

At the end of the lesson the student will be able to:

  • Explain the difference between call-by-value and call-by reference parameter passing
  • Return values from functions using call-by-reference

6.4.1: About Parameter Passing

  • Two ways to pass arguments to parameters
  • Call-by-value
    • A copy of the value is passed
  • Call-by-reference
    • The address of the actual argument is passed
  • All our functions so far have used call-by-value parameters

Call-by-Value Parameters

  • A copy of the actual argument is passed
  • Same as a local variable with special initialization inside the function
  • If modified, only the local copy changes
  • The function has no access to the actual argument from the caller
  • This is the default operation for function calls
  • Used in all our examples so far

Example of Call-ByValue Parameters

  • What does this program output?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int mystery(int param);

int main() {
    int num = 2;
    cout << "At first, num=" << num << endl;
    num = mystery(num);
    cout << "Later, num=" << num << endl;
    return 0;
}

int mystery(int param) {
    cout << "param=" << param << endl;
    param = param * 2;
    return param;
}

6.4.2: Call-By-Reference Parameters

  • C++ has another parameter-passing mechanism known as call-by-reference
  • Used to provide access to caller's actual argument inside a function
  • Caller's data can be modified by the called function!
  • Specified by an ampersand '&' after the type in the parameter list

For Example

  • Here is a program that outputs the same things as the previous example
  • What is different?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;

void mystery(int& param);

int main() {
    int num = 2;
    cout << "At first, num=" << num << endl;
    mystery(num);
    cout << "Later, num=" << num << endl;
    return 0;
}

void mystery(int& param) {
    cout << "param=" << param << endl;
    param = param * 2;
}

Call-By-Reference Details

  • What's really passed in?
  • A reference back to the caller's original argument!
  • Refers to memory location of the original argument
  • Called an address, which is a unique number referring to a place in memory
  • Note that arguments for call-by-reference parameters must be variables, not constants

6.4.3: Mixed Parameter Lists

  • You can combine parameter-passing mechanisms
  • Parameter lists can include pass-by-value and pass-by-reference parameters
  • As usual, the order of arguments in the list is critical:
  • void mixedCall(int& par1, int par2, double& par3);
    
  • Function call:
  • int arg1 = 0, arg2 = 1;
    double arg3 = 2.2;
    mixedCall(arg1, arg2, arg3);
    
  • arg1 must be integer type and is passed by reference
  • arg2 must be integer type and is passed by value
  • arg3 must be double type and is passed by reference

6.4.4: Summary

  • A formal parameter is a local variable with special initialization
  • The parameter is filled in with the actual argument during the function call
  • Call-by-value parameters are local copies of argument in the function body
    • The actual argument cannot be modified
  • Call-by-reference parameters allow you to change the variable used in the function call
    • Actual argument can be modified
  • '&' symbol (ampersand) identifies call-by-reference variables
  • Call-by-reference passes the memory address of actual argument
  • Whatever is done to a formal parameter in the function body, is actually done to the value at the memory location of the argument variable
    • Makes the parameter an alias for the original argument
  • Arguments for call-by-reference parameters must be variables, not constants
  • You can mix call-by-value and call-by-reference parameters in a function
  • void mixedCall(int& par1, int par2, double& par3);
    

Check Yourself

  1. What happens in a call when the function is defined for call by value?
  2. What happens in a call when the function is defined for call by reference?
  3. How do call-by-value and call-by reference differ?

Exercise 6.4

  1. Label this exercise: Exercise 6.4
  2. Complete the following and record the answers to the questions in exercise6.txt.

Specifications

Consider the following program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;

void swapValues(int& variable1, int& variable2);

int main() {
    int num1 = 1, num2 = 2;
    cout << "Original values: " << num1
         << " " << num2 << endl;
    swapValues(num1, num2);
    cout << "Swapped values:  " << num1
         << " " << num2 << endl;

    return 0;
}

void swapValues(int& variable1, int& variable2) {
    int temp = variable1;
    variable1 = variable2;
    variable2 = temp;
}

Q1: What does the program do?

Q2: What output do you get if you remove the ampersands '&' from the swapValues() parameter list?

Q3: How do call-by-value and call-by reference differ?

Wrap Up

Home | WebCT | Announcements | Day Schedule | Eve Schedule
Course info | Help | FAQ's | HowTo's | Links

Last Updated: October 27 2005 @16:24:00