Finally we can write our complete program:
#include <algorithm> #include <iomanip> #include <ios> #include <iostream> #include <stdexcept> #include <string> #include <vector> #include "grade.h" #include "Student_info.h"
using std::cin; using std::setprecision; using std::cout; using std::sort;
using std::domain_error; using std::streamsize; using std::endl; using std::string; using std::max; using std::vector; int main()
{
vector<Student_info> students; Student_info record;
string::size_type maxlen = 0; // the length of the longest name // read and store all the students data.
// Invariant: students contains all the student records read so far // maxlen contains the length of the longest name in students while (read(cin, record)) {
// find length of longest name
maxlen = max(maxlen, record.name.size()); students.push_back(record);
}
// alphabetize the student records
sort(students.begin(), students.end(), compare); // write the names and grades
for (vector<Student_info>::size_type i = 0; i != students.size(); ++i) {
// write the name, padded on the right to maxlen + 1 characters cout compute and write the grade
try {
double final_grade = grade(students[i]); streamsize prec = cout.precision(); cout
This program should be fairly straightforward to understand. As usual, we start with the
necessary includes and using-declarations. Of course, we need mention only those
headers and declarations that we use in this source file. In this program, we have our own headers to include as well as library headers. Those headers make available the definition
of the Student_info type, and declarations for the functions that we use to
manipulate Student_info objects and generate grades. The main function itself is the same as the one that we presented in §4.2.3/64.
4.6 Details
Program structure:
#include <system-header>
Angle brackets, < >, enclose system headers. System headers may or may not be implemented as files. #include "user-defined-header-file-name"
User-defined header files are included by enclosing the name in quotes. Typically, user-defined headers have a suffix of .h.
Header files should be guarded against multiple inclusion by wrapping the file in an #ifndef GUARD_header_name directive. Headers should avoid declaring names that they do not use. In particular, they should not include
using-declarations, but instead should prefix standard-library names with std:: explicitly.
Types:
T&
Denotes a reference to the type T. Most commonly used to pass a parameter that a function may change. Arguments to such parameters must be lvalues.
const T&
Denotes a reference to the type T that may not be used to change the value to which the reference is bound. Usually used to avoid cost of copying a parameter to a function.
Structures: A structure is a type that contains zero or more members. Each object of the structure type contains its
own instance of each of its members. Every structure must have a corresponding definition:
struct type-name {
type-specifier member-name;
...
}; // note the semicolon
Like all definitions, a structure definition may appear only once per source file, so it should normally appear in a properly guarded header file.
Functions: A function must be declared in every source file that uses it, and defined only once. The declarations and
definitions have similar forms:
ret-typefunction-name (parm-decls) // function declaration [inline] ret-typefunction-name (parm-decls) { // function definition
// function body goes here }
Here, ret-type is the type that the function returns, parm-decls is a comma-separated list of the types for the parameters of the function. Functions must be declared before they are called. Each argument's type must be compatible with the corresponding parameter. A different syntax is necessary to declare or define functions with sufficiently complicated return types; see §A.1.2/297 for the full story.
differ in the number or types of the parameters. The implementation can distinguish between a reference and a const reference to the same type.
We can optionally qualify a function definition with inline, which asks the compiler to expand calls to the function inline when appropriate—that is, to avoid function-call overhead by replacing each call to the function by a copy of the function body, modified as necessary. To do so, the compiler needs to be able to see the function definition, so inlines are usually defined in header files, rather than in source files.
Exception handling:
try { // code
Initiates a block that might throw an exception. } catch(t) { /* code* / }
Concludes the try block and handles exceptions that match the type t. The code following the catch performs whatever action is appropriate to handle the exception reported in t.
throw e;
Terminates the current function; throws the value e back to the caller.
Exception classes: The library defines several exception classes whose names suggest the kinds of problems they
might be used to report:
logic_error domain_error invalid_argument length_error out_of_range runtime_error range_error overflow_error underflow_error
e.what()
Returns a value that reports on what happened to cause the error.
Library facilities:
s1 < s2
Compares strings s1 and s2 by applying dictionary ordering. s.width(n)
Sets the width of stream s to n for the next output operation (or leaves it unchanged if n is omitted). The output is padded on the left to the given width. Returns the previous width. The standard output operators use the existing width value and then call width(0) to reset the width.
setw(n)
Returns a value of type streamsize (§3.1/36) that, when written on an output stream s, has the effect of calling s.width(n).
Exercises
4-0. Compile, execute, and test the programs in this chapter.
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;
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 (described above) to manage the output so that the values line up in columns.
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.
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.
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.
4-6. Rewrite the Student_info structure to calculate the grades immediately and store only the final grade. 4-7. Write a program to calculate the average of the numbers stored in a vector<double>.
4-8. If the following code is legal, what can we infer about the return type of f?