grade.h
#ifndef GUARD_grade_h
#define GUARD_grade_h
// grade.h
#include <vector>
double median(std::vector<double>);
double grade(double, double, double);
double grade(double, double, const std::vector<double>&);
double grade(const Student_info&);
#endif
grade.cpp
// source file for the grade-ralated functons
#include <vector>
#include <stdexcept>
#include "Student_info.h"
#include "median.h"
using std::vector; using std::domain_error; using std::sort;
double median(vector<double> vec) {
typedef vector<double>::size_type vec_sz;
vec_sz size = vec.size();
if (size == 0)
throw domain_error("median of an empty vector");
sort(vec.begin(), vec.end());
vec_sz mid = size / 2;
return size % 2 == 0 ? (vec[mid - 1] + vec[mid]) / 2 : vec[mid];
}
double grade(double midterm, double final, double homework) {
return 0.2 * midterm + 0.4 * final + 0.4 * homework;
}
double grade(double midterm, double final, const vector<double>& hw) {
if (hw.size() == 0)
throw domain_error("student has done no homework");
return grade(midterm, final, median(hw));
}
double grade(const Student_info& s) {
return grade(s.midterm, s.final, s.homework);
}
Student_info.h
#ifndef GUARD_Student_info
#define GUARD_Student_info
// Student_info.h header file
#include <iostream>
#include <string>
#include <vector>
class Student_info{
public:
std::string name() const {
return n;
}
std::istream& read(std::istream&);
double grade() const;
static bool compare(const Student_info&, const Student_info&);
private:
std::string n;
double midterm, final;
std::vector<double> homework;
};
#endif
Student_info.cpp
// source file for Student_info-related functions
#include <vector>
#include <iostream>
#include "Student_info.h"
#include "grade.h"
using std::cout; using std::vector; using std::istream;
double Student_info::grade() const {
return ::grade(midterm, final, homework);
}
bool Student_info::compare(const Student_info& x, const Student_info& y) {
return x.n < y.n;
}
istream& read_hw(istream& in, vector<double>& hw) {
// read and store the homework grades
cout << "Enter all your homework grades, "
"followd by end-of-file: ";
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& is) {
// read and store the student's name and midterm and final exam grades
cout << "Please enter your first name, midterm and final exam grades: ";
is >> n >> midterm >> final;
read_hw(is, homework);
return is;
}
Pic.h
#ifndef GUARD_Pic_h
#define GUARD_Pic_h
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include "Ptr.h"
// forward declaration
class Picture;
class Pic_base {
friend std::ostream& operator<<(std::ostream&, const Picture&);
friend class Frame_Pic;
friend class HCat_Pic;
friend class VCat_Pic;
friend class String_Pic;
private:
// no public interface(except for the destrctor)
typedef std::vector<std::string>::size_type ht_sz;
typedef std::string::size_type wd_sz;
virtual wd_sz width() const = 0;
virtual ht_sz height() const = 0;
virtual void display(std::ostream&, ht_sz, bool) const = 0;
public:
virtual ~Pic_base() { }
protected:
static void pad(std::ostream&, wd_sz, wd_sz);
};
void Pic_base::pad(std::ostream& os, wd_sz beg, wd_sz end) {
while (beg != end) {
os << " ";
++beg;
}
}
class Frame_Pic: public Pic_base {
friend Picture frame(const Picture&);
// no public interface
Ptr<Pic_base> p;
Frame_Pic(const Ptr<Pic_base>& pic): p(pic) { }
wd_sz width() const { return p->width() + 4; }
ht_sz height() const { return p->height() + 4; }
void display(std::ostream&, ht_sz, bool) const;
};
void Frame_Pic::display(std::ostream& os, ht_sz row, bool do_pad) const {
if (row >= height()) {
// out of range
if (do_pad)
pad(os, 0, width());
} else if (row == 0 || row == height() - 1) {
// top or bottom row
os << std::string(width(), '*');
} else if (row == 1 || row == height() - 2) {
// second from top or bottom row
os << "*";
pad(os, 1, width() - 1);
os << "*";
} else {
// interior row
os << "* ";
p->display(os, row - 2, true);
os << " *";
}
}
class VCat_Pic: public Pic_base {
friend Picture vcat(const Picture&, const Picture&);
Ptr<Pic_base> top, bottom;
VCat_Pic(const Ptr<Pic_base>& t, const Ptr<Pic_base>& b):
top(t), bottom(b) { }
wd_sz width() const {
return std::max(top->width(), bottom->width());
}
ht_sz height() const{
return top->height() + bottom->height();
}
void display(std::ostream&, ht_sz, bool) const;
};
void VCat_Pic::display(std::ostream& os, ht_sz row, bool do_pad) const {
wd_sz w = 0;
if (row < top->height()) {
// we are in the top subpicture
top->display(os, row, do_pad);
w = top->width();
} else if (row < height()) {
// we are in the bottom subpicture
bottom->display(os, row - top->height(), do_pad);
w = bottom->width();
}
if (do_pad)
pad(os, w, width());
}
class HCat_Pic: public Pic_base {
friend Picture hcat(const Picture&, const Picture&);
Ptr<Pic_base> left, right;
HCat_Pic(const Ptr<Pic_base>& l, const Ptr<Pic_base>& r):
left(l), right(r) { }
wd_sz width() const {
return left->width() + right->width();
}
ht_sz height() const {
return std::max(left->height(), right->height());
}
void display(std::ostream&, ht_sz, bool) const;
};
void HCat_Pic::display(std::ostream& os, ht_sz row, bool do_pad) const {
left->display(os, row, do_pad || row < right->height());
right->display(os, row, do_pad);
}
class String_Pic: public Pic_base {
friend class Picture;
std::vector<std::string> data;
String_Pic(const std::vector<std::string>& v): data(v) { }
wd_sz width() const;
ht_sz height() const { return data.size(); }
void display(std::ostream&, ht_sz, bool) const;
};
Pic_base::wd_sz String_Pic::width() const {
Pic_base::wd_sz n = 0;
for (Pic_base::ht_sz i = 0; i != data.size(); ++i)
n = std::max(n, data[i].size());
return n;
}
void String_Pic::display(std::ostream& os, ht_sz row, bool do_pad) const {
wd_sz start = 0;
// write the row if we're still in range
if (row < height()) {
os << data[row];
start = data[row].size();
}
// pad the output if necessary
if (do_pad)
pad(os, start, width());
}
class Picture {
friend std::ostream& operator<<(std::ostream&, const Picture&);
friend Picture frame(const Picture&);
friend Picture hcat(const Picture&, const Picture&);
friend Picture vcat(const Picture&, const Picture&);
public:
Picture(const std::vector<std::string>& v = std::vector<std::string>()): p(new String_Pic(v)) { }
private:
Picture(Pic_base* ptr): p(ptr) { }
Ptr<Pic_base> p;
};
Picture frame(const Picture& pic) {
return new Frame_Pic(pic.p);
}
Picture hcat(const Picture& l, const Picture& r) {
return new HCat_Pic(l.p, r.p);
}
Picture vcat(const Picture& t, const Picture& b) {
return new VCat_Pic(t.p, b.p);
}
std::ostream& operator<<(std::ostream& os, const Picture& picture) {
const Pic_base::ht_sz ht = picture.p->height();
std::endl;
for (Pic_base::ht_sz i = 0; i != ht; ++i) {
picture.p->display(os, i, false);
os << std::endl;
}
return os;
}
#endif
Ptr.h
#ifndef GUARD_Ptr_h
#define GUARD_Ptr_h
// Ptr.h
#include <iostream>
#include <stdexcept>
template <typename T> class Ptr {
public:
// new member to copy the object conditionally when needed
void make_unique() {
if (*refptr != 1) {
--*refptr;
refptr = new size_t(1);
p = p ? p->clone() : 0;
}
}
// same as Ref_handle
Ptr(): p(0), refptr(new size_t(1)) { }
Ptr(T* t): p(t), refptr(new size_t(1)) { }
Ptr(const Ptr& h): p(h.p), refptr(h.refptr) { ++*refptr; }
Ptr& operator=(const Ptr&);
~Ptr();
operator bool() const { return p; }
T& operator*() const;
T* operator->() const {
if (p)
return p;
throw std::runtime_error("unbound Ptr");
}
private:
T* p;
std::size_t* refptr;
};
template <typename T>
Ptr<T>& Ptr<T>::operator=(const Ptr& rhs) {
++*rhs.refptr;
// free the left-hand side, destroying pointers if appropriate
if (--*refptr == 0) {
delete refptr;
delete p;
}
// copy in values from the right-hand side
refptr = rhs.refptr;
p = rhs.p;
return *this;
}
template <typename T>
T& Ptr<T>::operator*() const {
if (p)
return *p;
throw std::runtime_error("unbound Ptr");
}
template <typename T>
Ptr<T>::~Ptr() {
if (--*refptr == 0) {
delete refptr;
delete p;
}
}
#endif
main.cpp
// main.cpp
#include "Pic.h"
// #include "median.h"
// #include "grade.h"
// #include "Ptr.h"
#include "Student_info.h"
#include <algorithm>
using std::vector; using std::string; using std::cin;
using std::cout; using std::endl; using std::sort;
Picture histogram(const vector<Student_info>& students) {
Picture names;
Picture grades;
// for each student
for (vector<Student_info>::const_iterator it = students.begin(); it != students.end(); ++it) {
// create vertically concatenated pictures of the names and grades
names = vcat(names, vector<string>(1, it->name()));
grades = vcat(grades,
vector<string>(1, " " + string(it->grade() / 5, '=')));
}
// horizontally concatenate the name and grade pictures to combine them
return hcat(names, grades);
}
int main() {
vector<Student_info> students;
Student_info s;
// read the names and grades
while (s.read(cin))
students.push_back(s);
// put the students in alphabetical order
sort(students.begin(), students.end(), Student_info::compare);
// write the names and histograms
cout << frame(histogram(students)) << endl;
return 0;
}