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