Lecture 8 - Sept 22, 2023

Summary

In this lecture, we discuss string streams. Then we start introducing classes.

Last lecture

Handling I/O errors.

Today

String streams and introduce classes.

Recap on flags

Remember .eof() and .fail() flags.

Write a program that reads numbers from a file and prints their sum. If it reads a non-integer number, ignore it. Numbers are written on separate lines.

Pacha’s note

I added #include <iostream>, #include <fstream> and using namespace std here to make the code compile. I also added an << endl after cerr << to avoid this in the terminal

lecture8 (main) $ ./example1.so 
Can't open filelecture8 (main) $ 

but to see this instead

lecture8 (main) $ ./example1.so 
Can't open file
lecture8 (main) $ 
#include <iostream>
#include <fstream>

using namespace std;

int main() {
        ifstream inputFile("myFile.txt");

        if (inFile.fail()) {
                cerr << "Can't open file" << endl;
                return 1;
        }

        int num = 0, sum = 0;

        while (!inFile.eof()) {
                inFile >> num;

                if (inFile.fail()) {
                        inFile.clear();
                        inFile.ignore(1000, '\n');
                } else {
                        sum += num;
                }

        }

        cout << "Reached end of file" << endl;
        cout << "The sum is " << sum << endl;
        return 0;
}

String streams

String streams are helpful when inputs are line-oriented (i.e., user will enter a line and program has to check if inputs on that line are correct).

String streams can be used to extract inputs in the line.

Pacha’s note

I added #include <iostream> and using namespace std here to make the code compile.

#include <sstream>
#include <string>
#include <iostream>

using namespace std;

int main() {
        int ID;
        
        string name;
        string inputLine = "1001 Joe";
        
        // create a string stream initialized with input line
        stringstream myStringStream(inputLine);

        myStringStream >> ID; // takes input into ID
        myStringStream >> name; // takes input into name

        cout << "Name: " << name << endl << "ID: " << ID << endl;

        myStringStream << name << ID; 

        // converts a string stream to a string to print it
        cout << myStringStream.str() << endl;

        return 0;
}

What happens if I read more out of myStringStream than there is? myStringStream.eof() will be true.

Getline function

getline is part of iostream and it can be used to grab an entire line.

...

string inputLine;

// entire line entered by user gets stored in inputLine
getline(cin, inputLine);

stringstream myStringStream(inputLine);

myStream >> ID;
myStream >> name;

...

Lab 3 will use string streams.

Introduction to classes

A class is a user defined data type.

A variable of that user-defined data type is called an object.

Declaring an object is called creating an instance or instantiation.

diagrams/lecture8-diagram1.svg

Example

Recall structs in C.

This is a data structure that stores data of different types using one variable name.

struct Student {
        string name;
        int ID;
}

struct Student x;

// access name of x using . operator
x.name = "Cindy";

The definition of a class goes to a header file (e.g. Student.h)

Student.h

class Student {
        private:
                int ID;
                string name;

        public:
                void setName(string name);
                void getName();
                void print();
};

This needs a diagram to explain the different parts.

diagrams/lecture8-diagram2.svg

private members can only be accessed within the class.

public members can be accessed outside the class.

Why? This is called encapsulation

  • Hide information related to the class (e.g., student ID).
  • It is not available outside the class.
  • It brings related data and functions together.
  • It organizes the code.

Class implementation

The class implementation goes in a source file and it has function definitions.

Pacha’s note

I added endl after the cout statements for the same reason as above.

Student.cpp

#include "Student.h"
#include <iostream>

// Setter or mutator function

// class name: Student
// scope operator: ::
// function name: setName
void Student::setName(string n) {
        // private member of Student class can be accessed
        // inside the class implementation
        name = n;
}

// Getter or accessor function

string Student::getName() {
        return name;
}

void Student::print() {
        cout << "Student name: " << name << endl;
        cout << "Student ID: " << ID << endl;
}

main.cpp

Instatiating and using objects here.

#include "Student.h"

int main() {
        // declare 2 objects and allocate memory for them
        Student x,y;

        x.setName("Kenya");
        y.setName("Christina");

        // x.ID = 2730; gives an error because we cannot access a private member

        // y.name = "Salma" same error

        y.print(); // wait! what is the value of ID in Student y?

        return 0;
}

ID in x and y are not initialized. The solution is defining constructors in the next lecture.

Ungraded homework

Solve the exercises of Chapter 2: C++ I/O

Exercise 1

Question 5 in Fall 2022 Midterm Exam [Easy]

Consider the following program.

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    int a;
    ifstream inFile;
    inFile.open("input.txt");
    if (inFile.fail()) {
        return 1;
    }
    while (1) {
        inFile >> a;
        if (inFile.fail()) {
            cout << "failed.." << endl;
            inFile.clear();
            inFile.ignore(100, '\n');
            continue;
        }
        cout << "a = " << a << endl;
        break;
    }
    return 0;
}

Given the following contents of “input.txt”, write the output.

Case 1:

1
2
3

Case 2:

a32
b86
3

Case 3:

a32
b86 3

Case 1

a = 1

Explanation:

  • The while (1) loop is an infinite loop that will continue until it’s explicitly broken.
  • If the file reading operation is successful, the program outputs the value of a to the console and then breaks out of the loop with break;.

Case 2

failed..
failed..
a = 3

Explanation:

  • The while(1) loop will continue until it’s explicitly broken.
  • The first two cases fail, because the program read a non-integer value.

Case 3

failed..
failed..
...
infinite output

Explanation:

  • The while(1) loop will continue until it’s explicitly broken.
  • It fails for the first two lines and then the loop continues.
Exercise 2

Now you remove the infile.clear() from the code, so the program becomes:

#include <fstream>
#include <iostream>
using namespace std;

int main() {
    int a;
    ifstream inFile;
    inFile.open("input.txt");
    if (inFile.fail()) {
        return 1;
    }
    while (1) {
        inFile >> a;
        if (inFile.fail()) {
            cout << "failed.." << endl;
            // inFile.clear(); // COMMENTED OUT
            inFile.ignore(100, '\n');
            continue;
        }
        cout << "a = " << a << endl;
        break;
    }
    return 0;
}

Case 1

a = 1

Case 2

failed..
failed..
...
infinite output

Explanation:

  • The input stream is not cleared, so the program will continue to read the same non-integer value.

Case 3

failed..
failed..
...
infinite output

Explanation:

  • Same as case 2.
Exercise 3

Question 2 in Fall 2021 Final Exam [Intermediate]

Write a C++ function void readInts() that repeatedly reads integers from the standard input (using cin) and then immediately outputs the input integer (using cout), one integer per line. When a '.' character is encountered, the function prints the message Done on a line by itself and returns. If the user enters any characters other than integer digits or the '.', the function prints the message Error on a line by itself and returns. You may assume the user will never enter eof.

Thus, for example, if the user enters 51 16 700 ., the function prints:

51
16
700
Done

However, if the user enters 101 21 13 abc 444, the function prints:

101
21
13
Error
#include <iostream>
#include <string>

using namespace std;

void readInts() {
        int num;
        cin >> num;

        while (!cin.fail()) {
                cout << num << endl;
                cin >> num;
        }

        cin.clear();

        string chr;
        cin >> chr;

        if (chr == ".") {
                cout << "Done" << endl;
        } else {
            cout << "Error" << endl;
        }
}

int main() {
        readInts();
        return 0;
}
Exercise 4

Question 3 in Fall 2018 Midterm Exam [Intermediate]

Consider the following program that uses stringstreams to read a command. The command has the following format:

count intArg

The command word is count and intArg is an integer argument. The command must have only one integer argument, e.g count 3.

The function handle_count performs the reading of the integer value. If the integer is valid, it returns true and updates the value of intArg. Otherwise, it returns false.

#include <iostream>
using namespace std;
#include <sstream>
#include <string>
// function prototype
bool handle_count( <fill in the blank> );

int main() {
    string line;
    string command;
    int intArg;
    getline(cin, line);
    stringstream lineStream(line);
    lineStream >> command;
    if (command == "count") {
        if (handle_count( <fill in the blank> )) {
            cout << "Integer argument is " << intArg << endl;
            return (0);
        } else {
            cout << "Invalid arguments" << endl;
            return (-1);
        }
    } else {
        cout << "Invalid command" << endl;
        return (-1);
    }
}
  1. Determine the number of arguments and the type of each argument and indicate them in the code above in the prototype of the function, handle_count. Further, indicate what parameters are passed to the function when it is invoked. Write your answers where indicated in the code above. You may not modify main by adding or removing line, other than by indicating the formal arguments in the function prototype and actual arguments of the function invocation.
  2. Write the header and body of the handle_count function below so it performs as indicated above.

Part 1

We need two arguments, a stringstream (lineStream) and an int (intArg).

#include <iostream>
using namespace std;
#include <sstream>
#include <string>
// function prototype
bool handle_count(stringstream& lineStream, int& intArg);

int main() {
    string line;
    string command;
    int intArg;
    getline(cin, line);
    stringstream lineStream(line);
    lineStream >> command;
    if (command == "count") {
        if (handle_count(lineStream, intArg)) {
            cout << "Integer argument is " << intArg << endl;
            return (0);
        } else {
            cout << "Invalid arguments" << endl;
            return (-1);
        }
    } else {
        cout << "Invalid command" << endl;
        return (-1);
    }
}

Part 2

#include <iostream>
using namespace std;
#include <sstream>
#include <string>
// function prototype
bool handle_count(stringstream& lineStream, int& intArg);

int main() {
    string line;
    string command;
    int intArg;
    getline(cin, line);
    stringstream lineStream(line);
    lineStream >> command;
    if (command == "count") {
        if (handle_count(lineStream, intArg)) {
            cout << "Integer argument is " << intArg << endl;
            return (0);
        } else {
            cout << "Invalid arguments" << endl;
            return (-1);
        }
    } else {
        cout << "Invalid command" << endl;
        return (-1);
    }
}

bool handle_count(stringstream& line, int& intArg) {
    line >> intArg;

    if (line.fail()) {
        return false;
    }

    string dummy;
    line >> dummy;

    if (line.fail()) {
        return true;
    } else {
        return false;
    }
}
Exercise 5

Question 6 in Fall 2019 Midterm Exam [Intermediate]

For each of the following main functions, indicate the output produced in response to the user entering 1 2 3 4 five on the keyboard followed by the Enter key. Choose only one answer.

Part 1

#include <iostream>
using namespace std;
int main() {
    int num = 0;
    int sum = 0;
    while (!cin.fail()) {
        cin >> num;
        sum = sum + num;
    }
    cout << sum << endl;
    return (0);
}
  1. 6
  2. 10
  3. 14
  4. None; the program runs in an infinite loop

Part 2

#include <iostream>
using namespace std;
int main() {
    int num = 0;
    int sum = 0;
    bool more = true;
    while (more) {
        cin >> num;
        if (cin.fail())
            more = false;
        else
            sum = sum + num;
    }
    cout << sum << endl;
    return (0);
}
  1. 6
  2. 10
  3. 14
  4. None; the program runs in an infinite loop

Part 1

Result: 10 or 14. In some compilers num will be unchanged and will be added to the sum again.

Part 2

Result: 10. more will be set to false after the first non-integer input.

Exercise 6

Question 7 in Fall 2019 Midterm Exam [Intermediate]

Write a C++ function void readInts() that repeatedly reads integers from the standard input (using cin) and then immediately outputs the input integer (using cout), one integer per line.

When the end-of-file is reached, the function prints the message “End of File Reached” on a line by itself before returning. If a non-integer is input the function should print the message “Invalid Input” on a line by itself, should discard the rest of the stream and should continue reading integers again until the end-of-file is reached.

::: {.callout-note icon=false collapse=“true”} ## Solution

#include <iostream>

using namespace std;

void readInts() {
    // pass number from cin
    int num;
    cin >> num;

    // print until end of file
    while (!cin.eof()) {
        if (cin.fail()) {
            cin.clear();
            cin.ignore(1000, '\n');
            cout << "Invalid Input" << endl;
            cin >> num;
        } else {
            cout << num << endl;
            cin >> num;
        }
    }
    cout << "End of File Reached" << endl;
}

int main() {
    readInts();
    return 0;
}