(updated )

Chapter 4 – Organizing Programs and Data

Solutions to exercises in Chapter 4 of Accelerated C++, “Organizing programs and data.”

Exercise 4-1

We noted in §4.2.3/65 that it is essential that the argument types in a call to max match exactly. Will the following code work? If there is a problem, how would you fix it?

int maxlen;
Student_info s;
max(s.name.size(), maxlen);

expand/collapse icon Hint

Consider the return type of s.name.size().

expand/collapse icon Solution

There is a problem with the code. The type returned from s.name.size() is string::size_type, which does not match the type of maxlen. To solve the problem, change maxlen‘s declaration to string::size_type.

#include <algorithm>
#include <string>
#include <vector>

#ifdef _MSC_VER
#undef max
#endif

using std::max;
using std::string;
using std::vector;

struct Student_info {
	string name;
	double midterm, final;
	vector<double> homework;
};

int main()
{
   string::size_type maxlen;
   Student_info s;
   max(s.name.size(), maxlen);
}

Exercise 4-2

Write a program to calculate the squares of int values up to 100. The program should write two columns: The first lists the value; the second contains the square of that value. Use setw to manage the output so that the values line up in columns.

expand/collapse icon Hint

Determine the widest outputs for both columns.

expand/collapse icon Solution

The widest number that is being squared is 100, which is three digits. 100 squared is 10000, which is 5 digits, so the solution belows sets a width of 4 prior to sending the base to output and set a width of 6 prior to sending the result to output. Setting the widths one larger than the values contained will preserve at least one space to the left of each value.

#include <iomanip>
#include <iostream>

using std::cout;
using std::endl;
using std::setw;

int main()
{
   for (int i = 1; i < 101; ++i)
   {
      cout << setw(4) << i << setw(6) << (i * i) << endl;
   }
}

When executed the output will look like the following (shortened for space):

   1     1
   2     4
   3     9
   4    16
   5    25
   6    36
   7    49
   8    64
   9    81
  10   100
...
  95  9025
  96  9216
  97  9409
  98  9604
  99  9801
 100 10000

Exercise 4-3

What happens if we rewrite the previous program to allow values up to but not including 1000 but neglect to change the arguments to setw? Rewrite the program to be more robust in the face of changes that allow i to grow without adjusting the setw arguments.

expand/collapse icon Hint

Determine the length of the longest base and longest result and store those maximum lengths before starting to send the results to output.

expand/collapse icon Solution

If we change the program to output squares of bases up to 999 but don’t change the values provided to setw, the columns will begin to run together when the number of digits in the squares begins to equal 6.

   1     1
   2     4
   3     9
   4    16
   5    25
   6    36
   7    49
   8    64
   9    81
  10   100
...
 995990025
 996992016
 997994009
 998996004
 999998001

There are several ways to solve this problem, and in later chapters you will learn about some facilities in the standard library that would help you discover the maximum lengths that you need to find. This solution implements a function, digits, that will return the number of digits in an integer. The program uses this function to find the length of the maximum base and the length of the square of the maximum base. It saves these lengths and uses them as parameters to setw.

#include <iomanip>
#include <iostream>

using std::cout;
using std::endl;
using std::setw;

/* Return the number of digits in n */
int digits(int n)
{
   int result = 0;

   while (n)
   {
      /* Add 1 to the result... */
      ++result;

      /* ...and remove a digit before the next pass. */
      n /= 10;
   }

   return result;
}

int main()
{
   const int max_base = 999;

   /* Find the number of digits in the highest base. */
   int max_base_width = digits(max_base);

   /* Find the number of digits in the highest square. */
   int max_result_width = digits(max_base * max_base);
   
   for (int i = 1; i <= max_base; ++i)
   {
      /* Add 1 to the maximum widths to allow for one 
      space for padding. */
      cout << setw(max_base_width + 1) << i 
         << setw(max_result_width + 1) << (i * i) << endl;
   }
}

When executed the output will look like the following (shortened for space):

   1      1
   2      4
   3      9
   4     16
   5     25
   6     36
   7     49
   8     64
   9     81
  10    100
...
 995 990025
 996 992016
 997 994009
 998 996004
 999 998001

Exercise 4-4

Now change your squares program to use double values instead of ints. Use manipulators to manage the output so that the values line up in columns.

expand/collapse icon Hint

Allow for fractional values, and change how you determine the number of digits in the highest base and square.

expand/collapse icon Solution

First, we’ll make a change to the digits function to allow for the number of decimal places we want to see on the final outputs. Next, we’ll use the setprecision manipulator to control the number of significant digits in the output.

#include <iomanip>
#include <iostream>

using std::cout;
using std::endl;
using std::setw;
using std::setprecision;

/* Return the number of digits in n */
int digits(double n, int places)
{
   /* Initialize result to the number of desired decimal 
   places. */
   int result = places;

   while (n >= 1)
   {
      /* Add 1 to the result... */
      ++result;

      /* ...and remove a digit before the next pass. */
      n /= 10;
   }

   return result;
}

int main()
{
   const double max_base = 99.9;

   /* Find the number of digits in the highest base. */
   int max_base_width = digits(max_base, 2);

   /* Find the size of the highest square, allowing for 
   2 decimal places in the result. */
   int max_result_width = digits(max_base * max_base, 2);
   
   for (double i = 1; i <= max_base; i += .1)
   {
      /* Add 2 to the maximum widths to allow for the decimal point and 
      one space for padding. */
      cout << setw(max_base_width + 2) << 
         setprecision(max_base_width) << i 
         << setw(max_result_width + 2) 
         << setprecision(max_result_width) << (i * i) << endl;
   }
}

When executed the output will look like the following (shortened for space):

     1       1
   1.1    1.21
   1.2    1.44
   1.3    1.69
   1.4    1.96
   1.5    2.25
   1.6    2.56
   1.7    2.89
   1.8    3.24
   1.9    3.61
     2       4
   2.1    4.41
   2.2    4.84
   2.3    5.29
   2.4    5.76
   2.5    6.25
...
  98.5 9702.25
  98.6 9721.96
  98.7 9741.69
  98.8 9761.44
  98.9 9781.21
    99    9801
  99.1 9820.81
  99.2 9840.64
  99.3 9860.49
  99.4 9880.36
  99.5 9900.25
  99.6 9920.16
  99.7 9940.09
  99.8 9960.04
  99.9 9980.01

Exercise 4-5

Write a function that reads words from an input stream and stores them in a vector. Use that function both to write programs that count the number of words in the input, and to count how many times each word occurred.

expand/collapse icon Hint

Review the implementation of read_hw in §4.1.3. Then, learn how to use separate compilation with your particular compiler.

expand/collapse icon Solution

This exercise asks for two programs that perform two different tasks sharing a single function. That suggest that we should partition the programs as described in §4.4.

First, create a header that declares the shared function’s signature.

#ifndef GUARD_read_words_h
#define GUARD_read_words_h

#include <vector>
#include <istream>
#include <string>

std::istream& read_words(std::istream& is, std::vector<std::string>& words);

#endif GUARD_read_words_h

Next, implement the read_words function in a source file.

#include "read_words.h"

using std::istream;
using std::string;
using std::vector;

istream& read_words(istream& in, vector<string>& words)
{
   if (in)
   {
      words.clear();
      string word;

      while (in >> word)
      {
         words.push_back(word);
      }

      in.clear();
   }
   return in;
}

Now the programs can use this implementation. The first program will create a vector of strings and call read_words, passing cin and the vector as parameters. The populated vector will return the number of words it contains from its size() function.

#include "read_words.h"
#include <vector>
#include <iostream>
#include <string>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
   vector<string> words;
 
   cout << "Enter a few words, followed by end-of-file: " << endl;;

   if (read_words(cin, words))
   {
      cout << "You entered " << words.size() << " words." << endl;
   }
}

The second program borrows from the solution to exercise 3-3.

#include "read_words.h"
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::sort;
using std::string;
using std::vector;

int main()
{
   vector<string> words;
 
   cout << "Enter a few words, followed by end-of-file: " << endl;;

   if (read_words(cin, words))
   {
      typedef vector<string>::size_type vec_sz;
      vec_sz size = words.size();

      // Check that the user entered some words
      if (size == 0)
      {
         cout << endl << "You didn't enter any words. "
            "Please try again." << endl;
         return 1;
      }

      // sort the words
      sort(words.begin(), words.end());

      string current_word;
      int count;

      // Set the initial word to the first word in the vector
      current_word = words[0];

      // Set the initial count for the first word
      count = 1;

      // Invariant: we have counted current_index of the total words
      // in the vector
      for (vec_sz current_index = 1; current_index < size; ++current_index)
      {
         // Report the count for the current word if it does not match
         // the word at the current index in the vector, and reset the
         // count to zero so that it will one when the variable is
         // incremented outside the if statement.
         if (current_word != words[current_index])
         {
            cout << "The word \"" << current_word << "\" appears "
               << count << " times." << endl;

            current_word = words[current_index];
            count = 0;
         }

         ++count;
      }

      // Report the count for the final word
      cout << "The word \"" << current_word << "\" appears "
         << count << " times." << endl;

   }
   else
   {
      cout << "An error occurred during input." << endl;
      return 2;
   }

   // We have reported the count of all the words in the vector, so exit.
   return 0;
}

Exercise 4-6

Rewrite the Student_info structure, and the read and grade functions, so that they calculate each student’s grades as part of reading the input, and store only the final grade.

expand/collapse icon No Solution Yet…

Check back for the solution later.

Exercise 4-7

Write a program to calculate the average of the numbers stored in a vector<double>.

expand/collapse icon No Solution Yet…

Check back for the solution later.

Exercise 4-8

If the following code is legal, what can we infer about the return type of f?

double d = f()[n];

expand/collapse icon No Solution Yet…

Check back for the solution later.