Overview

This lecture is dedicated to odds and ends that we didn't cover because they weren't critical. In fact, often, there are good reasons not to use them. But, they exist, and you'll sometimes see them in code, and so they are often nice to know.

switch statements

The switch statement is a bit of a holdover from the C language on which C++ is based, but it is something you'll probably see if you look at a lot of programs. You use a switch statement when you want to test a single variable or expression for several different cases. The hitch is that the variable pretty much needs to be an int or a char, which limits when switch can be used. Switch breaks things up into cases. You write

  switch(expr) {

... where expr is an expression of type int or char, and then list cases consisiting of possible values of expr. These cases must be constants, and each case is followed by a :, then a sequence of statements to be executed, and finally a break;. In other words, each case looks like this:

  case constk:
           stmt1
           stmt2
           ...
           stmtr
  break;

You can list as many of these cases as you want. You can also put

  default:
           stmt1
           stmt2
           ...
           stmtr
  break;

in as one "case". This is a catch-all that catches every situation in which expr didn't match one of the other cases. Here's an example program using switch. It reads a date in "mm/dd/yyyy" format and returns the date in "dd monthname, yyyy" format.

cin.get() and eof

When we read in a value to variable x with the statement

  cin >> x;

cin skips over leading whitespace in the input stream, meaning it skips over newline characters ('\n'), tabs ('\t'), and spaces (' '). This is a bit of a problem if you want to do something like count the number of lines in a file. You can read input a char at a time without skipping over whitespace using cin.get(), and you can read from ifstream object fin a char at a time without skipping over whitespace using fin.get().

So, suppose you wanted to read in a line of text from the user and print out how many characters were in the line, you'd write this:

  #include <iostream>
  using namespace std;

  int main() {
    int count = 0;
    for(char c = cin.get(); c != '\n'; c = cin.get())
      count++;
    cout << "There were " << count << " characters!" << endl;
    return 0;
  }

If you want to know if you're at the end of the file, but not cause your istream to enter an error state, it can sometimes be possible to use fin.eof(). This returns true if the next character to be read would be the end-of-file character. So, you could do this:

  #include <iostream>
  using namespace std;

  int main() {
    int count = 0;
    ifstream fin("someFile.txt");
    for(char c = fin.get(); !fin.eof(); c = fin.get())
      count++;
    cout << "There were " << count << " characters!" << endl;
    return 0;
  }

Caveats: The full behavior of fin.eof() is somewhat more complex: it will return true when a read (using the stream operator >> or using fin.get() ) has moved the file pointer forward so that the next character in the stream is the end-of-file marker. So, if you have just read the very last character in the file, fin.eof() will return true, even though you haven’t yet done a read that failed. However, in many cases (especially if using the stream >> operator) your program will only really observe this end-of-file after a failed read, because most files have a newline character at the end of the file. If you are using the >> stream operator, you will only move past this newline character when you try to read in the next value (like an integer or string) and fail.

Formatting numbers with cout (or any ofstream object)

Oftentimes the way cout decides to format things might not agree with the way we'd like to see them formatted. For example, if x is a double with value 4.0, cout will write "4". Or, for example, the number of decimal places cout shows might not be sufficient.

Formatting can be affected by "i/o manipulators." If you want to force cout to always show a decimal point, you "send" it the manipulator showpoint (which is included in the iostream library).

double x = 3.0;
cout << x << endl;
cout << showpoint;
cout << x << endl;

YIELDS

3
3.000000

If you want to set the precision to be displayed in printint a number of type double use the setprecision(k) manipulator, where k is the number of digits you want shown (which requires the iomanip library). For example:

double x = 3.1234567890123456789;
cout << x << endl;
cout << setprecision(15);
cout << x << endl;

YIELDS

3.123456
3.12345678901235

Remember! Include the library iomanip when using setprecision!

Exiting main() early

The "return 0;" at the end of your program exits your program. In fact, inside main you can stick a "return 0;" wherever you want and as often as you want, and it'll exit the program. For example, maybe you want to write a program that reads an integer k from the user and writes out 1/k. If the user enters zero, of course, there's a problem. Now we'll just castigate the user and exit the program if he does that! You may prefer to use "exit(1)" instead of "return 1", since when we eventually write code outside of the main() block "exit(1)" sill still exit the program, but "return 1" won't. The "exit" function is available from the "iostream" library, for example.

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

  int main() {
    // Get number from user
    cout << "Enter a non-zero integer: ";
    int k;
    cin >> k;
    
    // Deal with bad input
    if (k == 0) {
      cout << "You follow directions really badly..."
           << endl;
      return 1;
    }

    // Write out decimal approximation of 1/k
    cout << setprecision(20);
    cout << "1/" << k << " is "
         << 1/double(k) << endl;

    return 0;
  }

do-while Loops

We've spent a lot of time with while-loops and for-loops. Both of these loops test whether to continue, and then go through the loop body, i.e. the test is done at the beginning of the loop. A "do-while" loop allows you to put the test at the end of the loop. This is convenient for certain tasks. For example, we looked at a program that reads expressions like

1 + 5 + 3 + 48 + 32 =

and prints out the resulting sum. This can be done nicely with a do-while. First, the old way:

#include <iostream>
using namespace std;

int main() {
  int next, sum = 0;
  char op;

  // Must initialize with something other 
  // than '=' just to make sure we enter
  // the loop the first time.
  op = 'X';
  while(op != '=') {
    cin >> next;
    sum = sum + next;
    cin >> op;
  }
  cout << sum << endl;

  return 0;
}

Then, with do-while:

#include <iostream>
using namespace std;

int main() {
  int next, sum = 0;
  char op;

  do {
    cin >> next;
    sum = sum + next;
    cin >> op;
  } while(op != '=');
  
  cout << sum << endl;

  return 0;
}

In case you don't find that compelling, here's another example. Suppose we want to keep reading in ints from a list until we read a negative number.

Using just a while:

#include <iostream>
using namespace std;

int main() {
  int n;

  cin >> n;  // read n the first time
  while(n >= 0)
    cin >> n;
  return 0;
}

Then, with do-while:

#include <iostream>
using namespace std;

int main() {
  int n;

  do {
    cin >> n;
  } while(n >= 0);

  return 0;
}