Welcome to Lesson 18!


Learning Objectives
By the end of today's class, you should know...
  • What is the difference between pass by reference and pass by value?
  • How do you pass by reference?
  • What is a stream?
  • How do you read data from and write data to a file?

Announcements

  • Next Quiz on Thursday
    • Functions + File I/O
  • Lab 8 on Friday



Review Activity

1. Write the following functions:

Function 1

  • The function should be named formatPrice
  • It takes in a single double parameter for the price
  • It prints out a formatted price to the console, in the format: $X.XX
  • Returns nothing
Function 2
  • The function is named printGrade
  • It takes in a double for the score
  • Prints out the grade as A, B, C, D, or F
  • Returns nothing

2. Call the above functions passing them values of your choice:

#include <iostream>
using namespace std;

int main() {
    //call the functions here

}


3. Write the comment and prototype for the below function:

double mpg(double miles, double gallons)
{
    return miles / gallons;
}


Value Parameters

  • There are two ways to pass arguments to parameters
  • All our functions so far have used value parameters
  • Value parameters are separate variables from the ones in main() (or another calling function)
  • Modification of value parameters does not affect the original caller's value when the function returns

How Value Parameters Work

  • During the function call, your program copies the argument's value into the parameter variable
  • The scope of value parameters is the same as the scope for a local variable
  • If the parameter is assigned a new value, only the local copy changes
  • When the function returns, your program discards any value assigned to a parameter variable
  • The following example program uses value parameters
  • What does this program output?

Example of Value Parameters

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

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

int main() {
    int num = 2;

    cout << "At first, num=" << num << endl;
    int result = mystery(num);
    cout << "After calling, num=" << num << endl;
    cout << "And result=" << result << endl;

    return 0;
}


Reference Parameters

  • C++ has another parameter-passing mechanism known as call-by-reference
  • A reference parameter does not create a new variable, but refers to an existing variable instead
  • Any change in a reference parameter is actually a change in the variable to which it refers
  • We create a reference parameter by using an ampersand (&) between the parameter's type and name
    parameterType& parameterName
    
  • The following program shows an example of reference parameters
  • What is different?
  • What does this program output?

Example of Reference Parameters

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

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

int main() {
    int num = 2;

    cout << "At first, num=" << num << endl;
    int result = mystery(num);
    cout << "After calling, num=" << num << endl;
    cout << "And result=" << result << endl;

    return 0;
}

Call-By-Reference Details

  • What's really passed to the reference parameter?
  • A reference to the caller's original argument!
  • Essentially a reference parameter is another name for the original argument
  • With a reference parameter the code uses the original argument variable in calculations
  • Any change made to a reference parameter changes the original argument
  • Because C++ is passing variables, arguments for reference parameters must be variables and not constants

Mixed Parameter Lists

  • Parameter lists can include both value and reference parameters
  • As usual, the order of arguments in the list is critical
  • The following is a function signature with mixed parameter types:
    void mixedCall(int& par1, int par2, double& par3)
    
  • To call the function:
    int arg1 = 0, arg2 = 1;
    double arg3 = 2.2;
    mixedCall(arg1, arg2, arg3);
    
  • arg1 must be an integer type and is passed by reference
  • arg2 must be an integer type and is passed by value
  • arg3 must be a double type and is passed by reference

When to Use Reference Parameters

  • Reference parameters are usually more efficient than value parameters because they do not make copies of the parameters:
    • A program simply passes the memory address to the function
    • No new memory space is allocated and deallocated
  • Therefore, function calls using reference parameters usually operate faster
  • However, reference parameters restrict the arguments we can use for a function
  • Specifically, we must use a variable argument and not a literal or constant value
  • Usually, the best practice is to pass an object, like a String, by reference
  • Also, we should use value parameters unless a function needs to modify a parameter
  • The performance advantage of reference parameters for primitive types tends to be negligible
  • Thus, it is not worth restricting the call pattern of a function

Reference vs Value Video


Activity 18.1: Exploring Call by Reference (10 pts)

In this exercise we explore how call-by-reference parameters differ from call-by-value parameters.

Specifications

  1. Copy the following program into a text editor and save it as swap.cpp:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    #include <iostream>
    using namespace std;
    
    void swap(int var1, int var2);
    
    int main() {
        int num1 = 0, num2 = 0;
        cout << "Enter two integers: ";
        cin >> num1 >> num2;
    
        swap(num1, num2);
    
        cout << "After calling function:  "
             << num1 << " " << num2 << endl;
        return 0;
    }
    
    void swap(int var1, int var2) {
        int temp = var1;
        var1 = var2;
        var2 = temp;
    }
    
  2. Compile and run the starter program to make sure you entered it correctly. When you run the program, the output should look like this:
    Enter two integers: 1 2
    After calling function: 1 2
    

    Notice that num1 and num2 have the same values before and after calling the function swap(). Any value assigned to var1 and var2 have no effect on num1 and num2.

  3. Change your program by adding the four ampersands (&) circled below:

    The ampersands tell C++ to use call-by-reference when passing parameter values.

  4. Compile and run the modified program to make sure you made the changes correctly. When you run the program, the output should look like this:
    Enter two integers: 1 2
    After calling function: 2 1
    

    Notice that num1 and num2 have different values before and after calling the function swap(). Any value assigned to var1 and var2 change num1 and num2 respectively.

  5. Submit your final program source code to Catalyst.



Streams and File I/O


About Program I/O
  • Program I/O = Program Input/Output
    • Input to and output from programs
  • Input can be from a keyboard, mouse or file
  • Output can be to a display screen, printer or file
  • Note that files can be both input and output devices for programs
  • Advantages of file I/O:
    • Data still exists after the program ends
    • Input can be automated (rather than entered manually)
    • Output from one program can be input to another
  • To store and retrieve data in a file, we need two constructs:
    • A file
    • A file stream object
  • We will look at files first

Files

File: a collection of data stored under a common name on a storage medium.

  • Files provide long-term storage of large amounts of data
  • Usually, you store files on durable storage mediums
    • Magnetic disks
    • Optical disks
    • Flash storage, like USB storage devices
  • Files are a single sequence of bytes

    Byte 0 Byte 1 Byte 2 ... Byte n−1 End-of-file marker

  • Since the data arranged as bytes, any type of data can be stored in a file
  • The operating system keeps track of the number of bytes in a file
  • Files must have a name
    • Naming requirements depend on the underlying operating system (OS)
  • The operating system organizes files into directories

Types of Files

  • All data in a file is ultimately just zeros and ones
  • Each binary digit can have one of two values: 0 or 1
  • A bit is one binary digit
  • A byte is a group of eight bits
  • These binary digits may represent integer values or text characters
  • It is up to the program using the file to understand the meaning and internal format of the data
  • In general, programs interpret data using two broad categories: text and binary

Text Files

  • In text files, the bits represent printable characters
  • Files are usually stored as one byte per character (ASCII)
  • Each line is delimited by end-of-line characters:
    • Macintosh (before OS-X): "\r"
    • Unix: "\n"
    • Windows: "\r\n"
  • An example of a text file is source code
  • We can read text files because each byte is interpreted by a program as textual characters
  • Some of these programs, like TextPad, then display the textual data to your computer's screen
  • Since there are many programs that read and display text, text files are called human readable

Binary Files

  • Data other than text is usually referred to as binary data
  • Each bit represents some type of encoded information
    • Such as program instructions or integer data
  • Binary files are easily read by the computer but not by humans
  • The following table compares binary and text values saved in a file
  • First we consider the value "1234" as ASCII codes and compare these bits to a binary value of 1234
  • As we can see, the bit patterns are different for the same data when stored as text or binary

Comparing Binary and Textual Data

Description Byte 0 Byte 1 Byte 2 Byte 3
"1234" as char's '1' '2' '3' '4'
"1234" as ASCII codes (bytes) 49 50 51 52
"1234" as ASCII codes (bits) 00110001 00110010 00110011 00110100
(int) 1234 as binary bits 00000000 00000000 00000100 11010010


Streams

Stream: a one-way transmission path that either delivers data to a destination (screen, file, etc.) or that takes data from a source (keyboard, file, etc.)

  • A stream connects a program to an I/O object
  • Input stream: an object that provides a sequence of bytes to a program

  • Output stream: an object that accepts a sequence of bytes from a program

  • cin and cout are input and output streams

File Streams

File stream: a one-way transmission path used to connect a program to a file.

  • File streams can be either input or output streams
  • File input streams receive data from a file
  • File output streams send data to a file
  • File I/O uses streams of type ifstream and ofstream
  • To declare a file input stream, we write code like:
    ifstream fin; //declares a file input stream
  • Similarly, to declare a file input stream we write code like:
    ofstream fout; //declares a file output stream
  • Each file your program uses will need a separate file stream object
Streams and Objects

  • Streams are objects and thus cin and cout are objects
  • Objects are special variables that can have a function associated with them
  • To call a function of an object, we use the dot operator
  • An example of using the dot operator is with cin is shown below
cin.fail();

Example of File I/O

  • Let us consider an example that reads from a file and writes to a file
  • The program reads from a file named infile.txt, which contains the following values:
    10
    20
    30
    
  • After summing the values, the program writes them to a file named outfile.txt
  • Consider the following code and try to identify:
    1. What is the name of the input stream?
    2. Which line opens a file for reading
    3. What is the name of the output stream?
    4. Which line opens a file for writing
    5. Which line reads data from the input stream?
    6. Which lines write data to the output stream?

Example Program to Read and Write Files


/**
    Reads three numbers from the file infile.txt,
    sums the numbers, and writes the sum to the
    file outfile.txt.
*/
#include <fstream>   // for file I/O
#include <iostream>
#include <cstdlib>
using namespace std;

int main() {
    ifstream fin;
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file opening failed.\n";
        exit(-1);
    }

    ofstream fout;
    fout.open("outfile.txt");
    if (fout.fail()) {
        cout << "Output file opening failed.\n";
        exit(-1);
    }

    int first, second, third;
    fin >> first >> second >> third;
    fout << "The sum of the first 3\n"
         << "numbers in infile.txt\n"
         << "is " << (first + second + third)
         << endl;

    fin.close();
    fout.close();

    cout << "Processing completed\n";

    return 0;
}
Closing a Stream

  • After finishing reading and writing you should close the file streams
  • If you do not close an output stream, you may lose data stored in the output buffer
  • In addition, streams consume system resources and you should not keep open any more streams than needed

Procedure For File I/O

  1. Place the following include directives in your program file:
    #include <fstream>   // for file I/O
    #include <iostream>  // for cout
    #include <cstdlib>   // for exit()
    using namespace std;
    
  2. Declare names for input and output streams like:
    ifstream fin;
    ofstream fout;
    
  3. Connect each stream to a file using open() and check for failure:
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file failed to open.\n"; exit(-1);
    }
    
    fout.open("outfile.txt");
    if (fout.fail()) {
        cout << "Output file failed to open.\n";
        exit(-1);
    }
    
  4. If there is a failure, we need to check that the input file is stored in the same directory that the .cpp file is executing.
  5. Important: If you are using a Mac, you may need to add the following line of code to our program to verify where the .cpp file is executing so we know where to save our .txt file : std::system("pwd");
  6. Read or write the data:
    • Read from a file with fin like using cin:
      fin >> first >> second >> third;
      
    • Write to a file with fout like using cout:
      fout << "first = " << first << endl;
      
  7. Close the streams when finished reading and writing:
    fin.close();
    fout.close();
    

More Information


Activity 18.2: Two Numbers

  • In this exercise we write a program that copies two numbers from an input stream to an output stream.
  • Copy the following program into a C++ file, save it as copytwo.cpp, and then compile and run the starter program to make sure you copied it correctly.
    #include <iostream>
    using namespace std;
    
    int main() {
        // Enter your code here
    
        return 0;
    }
    
  • Then, let's create a text file to test our program. Open up Notepad or another text editor (Word is not a good choice). Name the file infile.txt. In the file, add a list of numbers each on their own line, like this:
10
20
  • Important: You need to save this file in the same directory that you are executing your C++ files.
  • We will read from this file after writing our program.

  • Place the following include directives in your source code file:
    #include <iostream>  // for cout
    #include <fstream>   // for file I/O
    #include <cstdlib>   // for exit()
    using namespace std;
    
  • Inside main(), declare names for the input and output streams:
    ifstream fin;
    ofstream fout;
    
  • Add code to connect each stream to a file using open() and check for failure:
    fin.open("infile.txt");
    if (fin.fail()) {
        cout << "Input file failed to open.\n";
        exit(-1);
    }
    
    fout.open("outfile.txt");
    if (fout.fail()) {
        cout << "Output file failed to open.\n";
        exit(-1);
    }
    
  • Add statements to read two numbers from the input stream. For example, here is possible code for reading the first number:
    int first;
    fin >> first;
    
  • Add statements to write the two numbers to the output stream. For example, here is possible code for writing the first number:
    fout << "first = " << first << endl;
    
  • Close the streams when finished reading and writing:
    fin.close();
    fout.close();
    
  • Compile and run your modified program to make sure you made the changes correctly.

    Notice that you do not see any output on the screen for file reading or writing. The output stream wrote the program output to the output file.

  • Using a text editor like Notepad open the output file you created and verify that two numbers were copied. Your output file should look like:
    first = 10
    second = 20
    
  • Notice how Notepad cannot display the end of line characters for Unix. Try opening the file using CodeBlocks for comparison purposes.
  • Save your program source code file to submit to Catalyst

Listing of copytwo.cpp



Wrap Up

  • What will be the value of the num variable after calling the following functions:

void doubleVal(int& value){
    value *= 2;
}

void tripleVal(int value) {
    value *= 3;
}

int main() {
    int num = 3;
    doubleVal(num);
    tripleVal(num);
    cout << num;
}


Upcoming Assignments
  • Assignment 18 due Thursday at 3:20pm
  • Quiz on Thursday
  • Lab 8 due Friday


~See You on Thursday!~