template specialization
when we pass clone a Vec<char> grument, the compiler will use this specialized version of clone. When we pass other types of pointers, it wil instantiate the general template form of clone.
template <typename T> T* clone (const T* tp) {
return tp->clone();
}
// the key to making Ptr< Vec<char> > work
tempalet<>
Vec<char>* clone(const Vec<char>* vp) {
return new Vec<char> (*vp);
}
Core.h
#ifndef GUARD_core_h
#define GUARD_core_h
// Core.h
#include <string>
#include <iostream>
#include <vector>
class Core {
public:
Core():midterm(0),final(0) { }
Core(std::istream& is) { read(is); }
std::string name() const;
virtual std::istream& read(std::istream&);
virtual double grade() const;
virtual Core* clone() const { return new Core(*this); }
protected:
// accessible to derived classes
std::istream& read_common(std::istream&);
double midterm,final;
std::vector<double> homework;
private:
// accessible only to Core
std::string n;
};
std::istream& read_hw(std::istream&, std::vector<double>&);
double grade(double, double, std::vector<double>);
//bool compare(const Core&, const Core&);
#endif
Core.cpp
// source file Core-related
#include "Core.h"
#include "/home/rhk/workspace/4_grade/median.h"
using std::string; using std::vector;
using std::istream;
string Core::name() const { return n; }
double Core::grade() const {
return ::grade(midterm, final, homework);
}
istream& Core::read_common(istream& in) {
// read and store the student's name and exam grades
in >> n >> midterm >> final;
return in;
}
istream& Core::read(istream& in) {
read_common(in);
read_hw(in, homework);
return in;
}
double grade(double midterm, double final, vector<double> homework) {
return 0.2 * midterm + 0.4 * final + 0.4 * median(homework);
}
// read homworks
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;
}
bool compare(const Core& c1, const Core& c2) {
return c1.name() < c2.name();
}
Grad.h
#ifndef GUARD_grad_h
#define GUARD_grad_h
#include "Core.h"
class Grad:public Core {
public:
Grad(): thesis(0) { }
Grad(std::istream& is) { read(is); }
double grade() const;
std::istream& read(std::istream&);
protected:
Grad* clone() const { return new Grad(*this); }
private:
double thesis;
};
#endif
Grad.cpp
#ifndef GUARD_grad_h
#define GUARD_grad_h
#include "Core.h"
class Grad:public Core {
public:
Grad(): thesis(0) { }
Grad(std::istream& is) { read(is); }
double grade() const;
std::istream& read(std::istream&);
protected:
Grad* clone() const { return new Grad(*this); }
private:
double thesis;
};
#endif
Handle.h
#ifndef GUARD_handle_h
#define GUARD_handle_h
#include <stdexcept>
template <class T> class Handle {
public:
Handle():p(0) { }
Handle(const Handle& s): p(0) { if (s.p) p = s.p->clone(); }
Handle& operator=(const Handle&);
~Handle() { delete p; }
Handle(T* t): p(t) { }
operator bool() const { return p; }
T& operator*() const;
T* operator->() const;
private:
T* p;
};
template <typename T>
Handle<T>& Handle<T>::operator=(const Handle& rhs) {
if (&rhs != this) {
delete p;
p = rhs.p ? rhs.p->clone() : 0;
}
return *this;
}
template <typename T>
T& Handle<T>::operator*() const {
if (p) return *p;
throw std::runtime_error("unbound Handle");
}
template <typename T>
T* Handle<T>::operator->() const {
if (p) return p;
throw std::runtime_error("unbound Handle");
}
#endif
Student_info.h
#ifndef GUARD_Student_h
#define GUARD_Student_h
// Student_info.h
#include <iostream>
#include <stdexcept>
#include "Handle.h"
#include "Core.h"
#include "Grad.h"
class Student_info {
public:
Student_info() { };
Student_info(std::istream& is) { read(is); }
std::istream& read(std::istream&);
std::string name() const {
if (cp) return cp->name();
throw std::runtime_error("uninitialized Student");
}
double grade() const {
if (cp) return cp->grade();
throw std::runtime_error("uninitialized Student");
}
static bool compare(const Student_info& s1, const Student_info& s2) {
return s1.name() < s2.name();
}
private:
// Handle<Core> cp;
Core* cp;
};
std::istream& Student_info::read(std::istream& is) {
char ch;
is >> ch;
if (ch == 'U')
cp = new Core(is);
else
cp = new Grad(is);
return is;
};
#endif
main.cpp
#include "Core.h"
#include "Student_info.h"
#include "Grad.h"
#include <algorithm>
#include <ios>
#include <iomanip>
#include <stdexcept>
using std::string; using std::cout; using std::cin;
using std::endl; using std::vector; using std::max;
using std::streamsize; using std::sort; using std::setprecision;
using std::domain_error;
int main() {
vector<Student_info> students;
Student_info record;
string::size_type maxlen = 0;
while (record.read(cin)) {
maxlen = max(maxlen, record.name().size());
students.push_back(record);
}
sort(students.begin(), students.end(), Student_info::compare);
for (vector<Grad>::size_type i = 0; i < students.size(); ++i) {
cout << students[i].name() << string(maxlen + 1 - students[i].name().size(), ' ');
try {
double final_grade = students[i].grade();
streamsize prec = cout.precision();
cout << setprecision(3) << final_grade <<
setprecision(prec) << endl;
} catch (domain_error e) {
cout << e.what() << endl;
}
}
return 0;
}