max function
#include <iostream>
#include <string>
using std::endl;
using std::cout;
using std::string;
template <class T>
T my_max(const T &left, const T &right)
{
return left > right ? left : right;
}
int main()
{
string s1 = "hello", s2 = "world";
cout << my_max(12, 15) << endl;
cout << my_max(1232.43, 54.2) << endl;
cout << my_max(s1, s2) << endl;
// Error: parameters are of different type
// cout << my_max(12, 15.0) << endl;
return 0;
}
find function
// This file contains the implementation of the find function.
// This function is not actually used elsewhere in the book.
template <class In, class X>
In find(In begin, In end, const X& x)
{
if (begin == end || *begin == x)
return begin;
begin++;
return find(begin, end, x);
}
The above generic function uses an input iterator. In C++ there are five categories of iterators. They are summarized on p.154 of K&M:
Student_info type
#ifndef GUARD_Student_info
#define GUARD_Student_info
#include <string>
#include <vector>
class Student_info {
public:
Student_info(); // construct an empty `Student_info' object
Student_info(std::istream&); // construct one by reading a stream
std::string name() const { return n; }
bool valid() const { return !homework.empty(); }
// as defined in 9.2.1/157, and changed to read into
// `n' instead of `name'
std::istream& read(std::istream&);
double grade() const; // as defined in 9.2.1/158
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
bool compare(const Student_info&, const Student_info&);
#endif
Student_info type
#include <iostream>
#include <vector>
#include "grade.h"
#include "Student_info.h"
using std::istream;
using std::vector;
double Student_info::grade() const
{
return ::grade(midterm, final, homework);
}
bool compare(const Student_info& x, const Student_info& y)
{
return x.name() < y.name();
}
Student_info::Student_info(): midterm(0), final(0) { }
Student_info::Student_info(istream& is) { read(is); }
// read homework grades from an input stream into a `vector<double>'
istream& read_hw(istream& in, vector<double>& hw)
{
if (in) {
// get rid of previous contents
hw.clear();
// read homework grades
double x;
while (in >> x)
hw.push_back(x);
// clear the stream so that input will work for the next student
in.clear();
}
return in;
}
istream& Student_info::read(istream& in)
{
in >> n >> midterm >> final;
read_hw(in, homework);
return in;
}
main program that uses the Student_info type
#include <algorithm>
#include <iomanip>
#include <ios>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "Student_info.h"
#include "median.h"
using std::cin; using std::cout;
using std::domain_error; using std::endl;
using std::setprecision; using std::setw;
using std::sort; using std::streamsize;
using std::string; using std::vector;
using std::max;
int main()
{
vector<Student_info> students;
Student_info record;
string::size_type maxlen = 0;
// read and store the data
while (record.read(cin)) { // changed
maxlen = max(maxlen, record.name().size()); // changed
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) {
cout << students[i].name() // this and the next line changed
<< string(maxlen + 1 - students[i].name().size(), ' ');
try {
double final_grade = students[i].grade(); // changed
streamsize prec = cout.precision();
cout << setprecision(3) << final_grade
<< setprecision(prec) << endl;
} catch (domain_error e) {
cout << e.what() << endl;
}
}
return 0;
}
new and delete operatorsIn C++, dynamic memory allocation is much cleaner than in C. For example, to allocate memory for an integer, initialize it and deallocated it, we would do this in C:
int *p = (int *) malloc(sizeof(int)); *p = 42; ... free(p);
However, in C++, the same thing can be accomplished much easier:
int *p = new int(42); ... delete p;
You can think of the int(42) initializer as passing
in the constant 42 to the int constructor.
An appropriate amount of memory will be allocated for the integer
and the value stored there will be initialized to 42.
We could have just allocated the memory as follows:
int *p = new int;
In the above case, the value pointed to by p will be
uninitialized and will likely be garbage. A value will have to be
assigned to this location (e.g. *p = 42) prior
to using *p in an expression.
Allocating an array in C++ is also easier than allocating an array
in C. For example, in C, we could use the following to allocate
an array of 100 integers (note that calloc() could
also be used).
int *p = (int *) malloc(sizeof(int) * 100); ... free(p);
In C++, we can use the following syntax to allocate an array of 100 integers:
int *p = new int[100]; ... delete [] p;
Note that when we construct an array in C++, we use the new
operator and specify the number of items to allocate in square brackets.
When we deallocate an array in C++, we must use empty square brackets
between the delete operator and the variable that points
to the array that we are deallocating.
Note that in the C, the array contents are not initialized to anything in particular -- the values in the array are undefined. In the C++ case the array elements are initialized by calling the default constructor for the type being defined. In the case of integers, the array elements are uninitialized.
Like Java, the standard library in C++ has an exception hierarchy.
At the top of this hierarchy is the exception class.
This class has two major subclasses, logic_error and
runtime_error (we'll be talking about subclasses in
subsequent lectures).
The logic_error class has four other subclasses:
length_error,
domain_error,
out_of_range and
invalid_argument.
The runtime_error class also has three other subclasses:
range_error,
overflow_error and
underflow_error.
In the catch clause of a try { ... } catch
block, the exceptions should be listed in increasing order of generality.
(i.e.subclass exceptions should be listed before their
parent class exceptions). When an exception is generated, the code
specified in an appropriate catch clause (if one exists)
will be executed. For example, if a domain_error is
thrown, it will be caught by a catch clause that catches a
domain_error exception. The error will also be caught, if,
instead of catching a domain_error, the catch
clause catches a logic_error. This is because, in the
context of the class hierarchy, a domain_error is-a
logic_error
Last modified: Mon Mar 10 12:57:34 2003