Now Available: Atomic Scala 2nd Edition eBook
Hands-On Java eSeminar
Thinking in Java 4e Electronic Book (PDF+HTML)  and Solutions Guide
Thinking in C++ Volume 1 Solutions Guide
             Home Blog Books eSeminars Letter Consulting Seminars About FAQ Search

Thinking in C++ Vol 1 Solutions Guide Sample

Back To Solution Main Page

Chapter 2

2-1

Modify Hello.cpp so that it prints out your name and age (or shoe size, or your dog’s age, if that makes you feel better). Compile and run the program.

Solution:

The original Hello.cpp appeared in the text as follws:

// Saying Hello with C++
#include <iostream> // Stream declarations
using namespace std;

int main() {
  cout << "Hello, World! I am "
    << 8 << " Today!" << endl;
}

Here’s my rewrite:

//: S02:Hello2.cpp
#include <iostream>
using namespace std;

int main() {
  cout << "Hello, World! I am Chuck Allison." << endl;
  cout << "I have two dogs:" << endl;
  cout << "Sheba, who is " << 5 << ", and" << endl;
  cout << "Muffy, who is 8." << endl;
  cout << "(I feel much better!)" << endl;
}

/* Output:
Hello, World! I am Chuck Allison.
I have two dogs:
Sheba, who is 5, and
Muffy, who is 8.
(I feel much better!)
*/ ///:~

I chose to have separate statements that send output to cout, but I could have printed everything in a single statement if I had wanted, like the example in the text does. Note that in the case of Sheba’s age, I printed 5 as an integer, but for Muffy I included the numeral in the literal text. In this case it makes no difference, but when you print floating-point numbers that have decimals, you get 6 decimals by default. Bruce discusses later in the text how to control output of floating-point numbers.

2-2

Starting with Stream2.cpp and Numconv.cpp, create a program that asks for the radius of a circle and prints the area of that circle. You can just use the ‘*’ operator to square the radius.

Solution:

The two programs mentioned above show how to do numeric input and output. The following solution to this exercise likewise uses the left-shift and right-shift operators for input and output, respectively.

//: S02:Area.cpp
//{-T} @echo run Area by hand!
#include <iostream>
using namespace std;

int main() {
  const float pi = 3.141592654;
  cout << "Enter the radius: ";

  float radius;
  cin >> radius;
  cout << "The area is " << pi * radius * radius << endl;
}

/* Sample Execution:
c:>area
Enter the radius: 12
The area is 452.389
*/ ///:~

The const keyword declares that the float variable pi will not be changed during the execution of the program. Note that it is not necessary to insert a newline (via endl, say) after printing the prompt. That’s because cin is tied to cout, which means that cout always gets flushed right before you read from cin.

2-3

Create a program that opens a file and counts the whitespace-separated words in that file.

Solution:

The first version opens its own source file:

//: S02:Words.cpp
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

int main() {
  ifstream f("Words.cpp");
  int nwords = 0;
  string word;

  while (f >> word)
    ++nwords;

  cout << "Number of words = " << nwords << endl;
}

/* Output:
Number of words = 42
*/ ///:~

The <fstream> header defines classes for file IO, including ifstream, whose constructor takes a file name an argument. The expression f >> word extracts the next non-whitespace token from the file and returns the stream. When a stream appears in a boolean context, such as the while statement above, it evaluates to true until end-of-file reached or an error occurs.

It turns out that on many operating systems you can take advantage of i/o redirection, which allows you to map standard input or output to a file on the command line. If you rewrite Words.cpp to read from cin, then you can read any file you want, as the following illustrates.

//: S02:Words2.cpp
//{T} < Area.cpp
#include <iostream>
#include <string>
using namespace std;

int main() {
  int nwords = 0;
  string word;

  while (cin >> word) {
    ++nwords;
  }

  cout << "Number of words = " << nwords << endl;
}

/* Sample Execution:
c:>words < Area.cpp
Number of words = 41
*/ ///:~

The less-than symbol redirects standard input to come from the file Area.cpp, so that when you run the program, cin is hooked to that file. The greater-than symbol does the same for cout.

2-4

Create a program that counts the occurrence of a particular word in a file (use the string class’ operator ‘==’ to find the word).

Solution:

//: S02:WordCount.cpp
// Counts the occurrences of a word
#include <iostream>
#include <fstream>
#include <string>

int main(int argc, char* argv[]) {
  using namespace std;

  // Process command-line arguments:
  if (argc < 3) {
    cerr << "usage: WordCount word file\n";
    return -1;
  }
  string word(argv[1]);
  ifstream file(argv[2]);

  // Count occurrences:
  long wcount = 0;
  string token;
  while (file >> token)
    if (word == token)
      ++wcount;

  // Print result:
  cout << '"' << word << "\" appeared "
       << wcount << " times\n";
} ///:~

This program uses the argc and argv arguments to main, which represent the argument count and array of pointers to the arguments, respectively. If you don’t provide a word to search for and a file to search in, you get an error message and the program quits. The extraction operator for input streams skips whitespace when filling a string object, so token takes on each such “word” in the file in succession. When run on its own text you get the following output:

"word" appeared 3 times

Notice that in this example I chose to put the using directive in main, instead of at file scope. In these simple examples it really doesn’t matter where you put it, but in examples where you have many functions, it’s sometimes important to only put the directive inside the functions that need it, to minimize the possibility of name clashes. Bruce discusses this in more detail in Chapter 4.

2-5

Change Fillvector.cpp so it prints the lines (backwards) from last to first.

Solution:

This modification to FillVector.cpp from the book requires only that you change the index to the vector from i to v.size()-i-1.

//: S02:Fillvector.cpp
// Copy an entire file into a vector of string
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

int main() {
  vector<string> v;
  ifstream in("Fillvector.cpp");
  string line;
  while(getline(in, line))
    v.push_back(line);

  // Print backwards:
  int nlines = v.size();
  for(int i = 0; i < nlines; i++) {
    int lineno = nlines-i-1;
    cout << lineno << ": " << v[lineno] << endl;
  }
} ///:~

2-6

Change Fillvector.cpp so it concatenates all the elements in the vector into a single string before printing it out, but don’t try to add line numbering.

Solution:

Following the example of FillString.cpp, the program below appends each string in the vector to a string variable (lines), following each with a newline.

//: S02:Fillvector2.cpp
// Copy an entire file into a vector of string
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

int main() {
  vector<string> v;
  ifstream in("Fillvector2.cpp");
  string line;
  while(getline(in, line))
    v.push_back(line);

  // Put lines into a single string:
  string lines;
  for(int i = 0; i < v.size(); i++)
    lines += v[i] + "\n";
  cout << lines;
} ///:~


2-7

Display a file a line at a time, waiting for the user to press the “Enter” key after each line.

Solution:

To make this program work as advertised, it’s important to not follow each display of a line with a newline character, otherwise the output will appear double-spaced.

//: S02:FileView.cpp
// Displays a file a line at a time
#include <string>
#include <iostream>
#include <fstream>
using namespace std;

int main() {
  ifstream in("FileView.cpp");
  string line;
  while(getline(in, line)) {
    cout << line;  // No endl!
    cin.get();
  }
} ///:~

The call to cin.get( ) waits for you to press Enter and consumes a single character (the newline that results from pressing Enter).

2-8

Create a vector<float> and put 25 floating-point numbers into it using a for loop. Display the vector.

Solution:

//: S02:FloatVector.cpp
// Fills a vector of floats
#include <iostream>
#include <vector>
using namespace std;

int main() {
  // Fill vector:
  vector<float> v;
  for (int i = 0; i < 25; ++i)
    v.push_back(i + 0.5);

  // Display
  for (int i = 0; i < v.size(); ++i) {
    if (i > 0)
      cout << ' ';
    cout << v[i];
  }
  cout << endl;
}

/* Output:
0.5 1.5 2.5 3.5 4.5 5.5 6.5 7.5 8.5 9.5 10.5 11.5
12.5 13.5 14.5 15.5 16.5 17.5 18.5 19.5 20.5 21.5
22.5 23.5 24.5
*/ ///:~

In this solution I decided to separate the numbers by spaces, to take up fewer lines in the output. I could have used 25 as the limit of the second for loop, but it’s better to use vector::size( ), since that works no matter how many elements you process.

2-9

Create three vector<float> objects and fill the first two as in the previous exercise. Write a for loop that adds each corresponding element in the first two vectors and puts the result in the corresponding element of the third vector. Display all three vectors.

Solution:

//: S02:FloatVector2.cpp
// Adds vectors
#include <iostream>
#include <vector>
using namespace std;

int main() {
  vector<float> v1, v2;
  for (int i = 0; i < 25; ++i) {
    v1.push_back(i);
    v2.push_back(25-i-1);
  }

  // Form sum:
  vector<float> v3;
  for (int i = 0; i < v1.size(); ++i)
    v3.push_back(v1[i] + v2[i]);

  // Display:
  for (int i = 0; i < v1.size(); ++i) {
    cout << v1[i] << " + " << v2[i]
         << " = " << v3[i] << endl;
  }
}

/* Output:
0 + 24 = 24
1 + 23 = 24
2 + 22 = 24
3 + 21 = 24
4 + 20 = 24
5 + 19 = 24
6 + 18 = 24
7 + 17 = 24
8 + 16 = 24
9 + 15 = 24
10 + 14 = 24
11 + 13 = 24
12 + 12 = 24
13 + 11 = 24
14 + 10 = 24
15 + 9 = 24
16 + 8 = 24
17 + 7 = 24
18 + 6 = 24
19 + 5 = 24
20 + 4 = 24
21 + 3 = 24
22 + 2 = 24
23 + 1 = 24
24 + 0 = 24
*/ ///:~

It is crucial, of course, that you use push_back to fill v3. Mathematically we think of the operation as:

v3[i] = v1[i] + v2[i];  // wrong!

but v3[i] does not exist unless you make space for it, which is exactly what push_back does. If you want to use the above expression instead, you can “size” the vector with the resize method, as follows:

  // Form sum:
  vector<float> v3;
  v3.resize(v1.size());  // pre-allocate space
  for (int i = 0; i < v1.size(); ++i)
    v3[i] = v1[i] + v2[i];

When you call resize on a vector, it truncates the sequence if the new size is smaller, or it appends “zeroes”, if the number is larger, where “zero” means the appropriate value for the type of contained element. You can even resize vectors of strings and get empty strings added. Try it!

2-10

Create a vector<float> and put 25 numbers into it as in the previous exercises. Now square each number and put the result back into the same location in the vector. Display the vector before and after the multiplication.

Solution:

//: S02:FloatVector3.cpp
// Squares a vector of floats
#include <iostream>
#include <vector>
using namespace std;

int main() {
  // Fill and print:
  vector<float> v;
  for (int i = 0; i < 25; ++i)
    v.push_back(i);
  for (int i = 0; i < v.size(); ++i) {
    if (i > 0)
      cout << ' ';
    cout << v[i];
  }

  // Square and print:
  for (int i = 0; i < v.size(); ++i)
    v[i] = v[i] * v[i];
  for (int i = 0; i < v.size(); ++i) {
    if (i > 0)
      cout << ' ';
    cout << v[i];
  }
} ///:~

This might be a good time to mention a work-around for compilers that don’t support reusing for-loop variables, like I did with i above. If you have such a compiler, simple define the following macro:

#define for if(0); else for

This puts all for statements in the else-part of an if-else, where the if-part is empty. As long as you compiler does proper scoping for if-else statements, this will allow you to reuse loop indices until your compiler comes up with a standards-compliant version.

Back To Solution Main Page

Search Home Books eSeminars Letter Consulting Seminars About Contact Site Design
©2014 MindView LLC