Accelerated C++ 习题解答 第13章

来源:互联网 发布:怎么学会c语言 编辑:程序博客网 时间:2024/05/15 03:07

习题解答部分来自github


EX.13-0

Core.h

#ifndef GUARD_Core_h#define GUARD_Core_h#include <iostream>#include <stdexcept>#include <string>#include <vector>using namespace std;class Core {public:Core(): midterm(0), final(0) { }Core(std::istream& is) { read(is);}std::string name() const;// as defined in 13.1.2virtual std::istream& read(std::istream&);virtual double grade() const;virtual ~Core() { }protected:// accessible to derived classesstd::istream& read_common(std::istream&);double midterm, final;std::vector<double> homework;virtual Core* clone() const { return new Core(*this); }private:// accessible only to `Core'std::string n;friend class Student_info;//将Student_info声明为Core类的友元类,这样,Studnet_info类就可以调用clone函数了};class Grad: public Core {public:Grad(): thesis(0) { }Grad(std::istream& is) { read(is); }// as defined in 13.1.2; Note: `grade' and `read' are `virtual' by inheritancedouble grade() const;std::istream& read(std::istream&);private:double thesis;//想基类中加入clone这个虚拟函数之后,一定要在其派生类中重定义这个函数Grad* clone() const { return new Grad(*this); }};bool compare(const Core&, const Core&);bool compare_Core_ptrs(const Core* cp1, const Core* cp2);#endif

Core.cpp

#include <algorithm>#include "Core.h"#include "grade.h"using std::istream;using std::string;using std::vector;using std::min;std::istream& read_hw(std::istream& in, std::vector<double>& hw);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 gradesin >> n >> midterm >> final;return in;}istream& Core::read(istream& in){read_common(in);read_hw(in, homework);return in;}istream& Grad::read(istream& in){read_common(in);in >> thesis;read_hw(in, homework);return in;}double Grad::grade() const{return min(Core::grade(), thesis);}bool compare(const Core& c1, const Core& c2){return c1.name() < c2.name();}bool compare_Core_ptrs(const Core* cp1, const Core* cp2){return compare(*cp1, *cp2);}

median.h

#ifndef GUARD_median_h#define GUARD_median_h#include <vector>double median(std::vector<double>);#endif

median.cpp

// source file for the `median' function#include <algorithm>    // to get the declaration of `sort'#include <stdexcept>    // to get the declaration of `domain_error'#include <vector>       // to get the declaration of `vector'using std::domain_error;   using std::sort;   using std::vector;#include "median.h"// compute the median of a `vector<double>'// note that calling this function copies the entire argument `vector'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] + vec[mid-1]) / 2 : vec[mid];}

grade.h

#ifndef GUARD_grade_h#define GUARD_grade_h#include <vector>double grade(double, double, double);double grade(double, double, const std::vector<double>&);#endif

grade.cpp

#include <vector>#include <stdexcept>using std::domain_error;using std::vector;#include "grade.h"#include "median.h"// compute a student's overall grade from midterm and final exam grades and homework gradedouble grade(double midterm, double final, double homework){return 0.2 * midterm + 0.4 * final + 0.4 * homework;}// compute a student's overall grade from midterm and final exam grades// and vector of homework grades.// this function does not copy its argument, because `median' does so for us.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));}

read_hw.cpp

#include <iostream>#include <vector>using std::istream;using std::vector;// 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 contentshw.clear();// read homework gradesdouble x;while (in >> x)hw.push_back(x);// clear the stream so that input will work for the next studentin.clear();}return in;}

Student_info.h

#ifndef GUARD_Student_info_h#define GUARD_Student_info_h#include <iostream>#include <stdexcept>#include <string>#include <vector>#include "Core.h"class Student_info {public:// constructors and copy control//两个构造函数都将cp指针初始化为0,表明Student_info对象尚未指定Student_info(): cp(0) { }Student_info(std::istream& is): cp(0) { read(is); }//下面是“三位一体”Student_info(const Student_info&);Student_info& operator=(const Student_info&);~Student_info() { delete cp; }// operationsstd::istream& read(std::istream&);std::string name() const {if (cp) return cp->name();else throw std::runtime_error("uninitialized Student");}double grade() const {if (cp) return cp->grade();else throw std::runtime_error("uninitialized Student");}//静态成员函数具有一个重要的好处,它们的函数名带有它们所属类的限定词//当我们声明compare为一个静态成员函数时,就是在声明一个称为Student_info::compare的函数//由于该函数有一个带限定词的名称,因此它不会重载我们用于比较Core类型对象的compare函数static bool compare(const Student_info& s1, const Student_info& s2) {return s1.name() < s2.name();}private:Core* cp;};#endif

Studnet_info.cpp

#include <iostream>#include "Core.h"#include "Student_info.h"using std::istream;//1)释放该句柄指向的对象(我们无需在调用delete之前先检测cp是否为0,在C++中删除一个零指针式无害的)//2)判断将要读的对象所具有的类型//3)为正确类型的对象分配合适大小的内存空间istream& Student_info::read(istream& is){delete cp;          // delete previous object, if anychar ch;is >> ch;           // get record typeif (ch == 'U') {cp = new Core(is);} else {cp = new Grad(is);}return is;}//复制构造函数Student_info::Student_info(const Student_info& s): cp(0){if (s.cp) cp = s.cp->clone();}//赋值运算符函数Student_info& Student_info::operator=(const Student_info& s){if (&s != this) {delete cp;if (s.cp)cp = s.cp->clone();elsecp = 0;}return *this;}

13_0.cpp

/*在C++中一个通用的技术是定义包装(cover)类或句柄(handle)类,也称智能指针。句柄类存储和管理基类指针。指针所指向对象的类型可以变化,它既可以指向基类类型对象又可以指向派生类型对象。用户通过句柄类访问继承层次的操作。因为句柄类使用指针执行操作,虚成员的行为将在运行时根据句柄实际绑定的对象类型而变化,即实现c++运行时动态绑定。故句柄用户可以获得动态行为但无需操心指针的管理。*/#include <algorithm>#include <iomanip>#include <ios>#include <iostream>#include <stdexcept>#include <string>#include <vector>#include <conio.h>#include "Student_info.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函数之后,在record中保存了一个指向该新建对象的指针//将这个对象复制到向量中时,作为副作用,Student_info的复制构造函数将被调用(此时student[i]与record并不是指向同一个对象)while (record.read(cin)) {maxlen = max(maxlen, record.name().size());students.push_back(record);}// alphabetize the student recordssort(students.begin(), students.end(), Student_info::compare);// write the names and gradesfor (vector<Student_info>::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();//grade函数使用指针调用底层对象的虚拟函数gradestreamsize prec = cout.precision();cout << setprecision(3) << final_grade     << setprecision(prec) << endl;} catch (domain_error e) {cout << e.what() << endl;}}getch();//当退出主函数时,在read函数里为Student_info类中的成员建立并分配的内存空间对象会被自动释放//向量对象将被删除,向量类的析构函数将删除studnets中的每个元素,这又会调用Studnet_info类的析构函数//这些析构函数运行之后,在read函数中建立并分配内存的对象都将被删除return 0;}


EX.13-1

在各构造函数中加入输出函数后,用下面函数进行测试

#include "Core.h"using namespace std;int main(){Core c1;Grad g1;Core c2(cin);Grad g2(cin);getch();return 0;}

EX.13-2

Core* p1 = new Core;Core* p2 = new Grad;Core s1;Grad s2;p1->grade();//调用Core::grad()p1->name();//调用Core::name()p2->grade();//调用Grad::grad()p2->name();//调用Core::name()s1.grade();//调用Core::grad()s1.name();//调用Core::name()s2.grade();//调用Crad::grad()s2.name();//调用Core::name()

EX.13-3----EX.13-8

Core.h

#pragma once#include <algorithm>#include <iostream>#include <string>#include <vector>#include "grade.h"class Core {friend class Student_info;public:Core(): midterm(0), final(0) { }Core(std::istream& is) { read(is); }std::string name() const { return n; }virtual std::istream& read(std::istream&);virtual double grade() const { return ::grade(midterm, final, homework); }//检测该对象是否存储着一个学生记录virtual bool valid() const { return !homework.empty(); }//检测学生是否完成了所有的家庭作业virtual bool fulfill_reqs() const {return (std::find(homework.begin(), homework.end(), 0.0) == homework.end());}virtual ~Core() { }protected:std::string n;double midterm, final;std::vector<double> homework;std::istream& read_common(std::istream&);virtual Core* clone() const { return new Core(*this); }};class Grad: public Core {public:Grad(): thesis(0) { }Grad(std::istream& is) { read(is); }std::istream& read(std::istream&);double grade() const { return std::min(Core::grade(), thesis); }////除了检测家庭作业外,研究生还要检测是否完成了论文bool fulfill_reqs() const { return (thesis > 0.0); }//private:double thesis;Grad* clone() const { return new Grad(*this); }};class PassFail: public Core {public:PassFail() { }PassFail(std::istream& is) { Core::read(is); }double grade() const {if (homework.size() > 0) return ::grade(midterm, final, homework);else return (midterm + final) / 2;}bool valid() const { return true; }  bool fulfill_reqs() const { return true; }private:PassFail* clone() const { return new PassFail(*this); }};//class Audit: public Core {public:Audit() { }Audit(std::istream& is) { read(is); }std::istream& read(std::istream&);double grade() const { return 0.0; }bool valid() const { return true; }  bool fulfill_reqs() const { return true; }private:Audit* clone() const { return new Audit(*this); }};bool compare(const Core&, const Core&);bool compare_Core_ptrs(const Core*, const Core*);std::string letter_grade(double);

Core.cpp

#include "Core.h"using namespace std;std::istream& read_hw(std::istream& in, std::vector<double>& hw);istream& Core::read_common(istream& in) {in >> n >> midterm >> final;return in;}istream& Core::read(istream& in) {read_common(in);read_hw(in, homework);return in;}istream& Grad::read(istream& in) {read_common(in);in >> thesis;read_hw(in, homework);return in;}istream& Audit::read(istream& in) {in >> n;return in;}bool compare(const Core& c1, const Core& c2) {return c1.name() < c2.name();}bool compare_Core_ptrs(const Core* cp1, const Core* cp2) {return compare(*cp1, *cp2);}string letter_grade(double grade) {static const double numbers[] = {97, 94, 90, 87, 84, 80, 77, 74, 70, 60, 0};static const char* const letters[] = {"A+", "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D", "F"};static const size_t ngrades = sizeof(numbers) / sizeof(*numbers);for (size_t i = 0; i < ngrades; ++i)if (grade >= numbers[i]) return letters[i];return "?\?\?";}

main_test.cpp

#include <algorithm>#include <iomanip>#include <iostream>#include <stdexcept>#include <string>#include <vector>#include <conio.h>#include "Student_info.h"using namespace std;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<Student_info>::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) << "\t" << letter_grade(final_grade) << endl;} catch (domain_error e) {cout << e.what() << endl;}}getch();return 0;}

EX.13-9

如果不能判断一个赋值操作是否是自我赋值,那么程序总会调用delete cp来删除cp指针所指向的(左操作数)对象的元素并释放其占用的内存。如果左右操作数指向同一个对象,那么右操作数也同时被删除。此时如果还用右操作数来为左操作数生成新的对象,将会带来一场灾难:在释放左操作数对象的内存空间时,我们已经将右操作数对象的空间也释放了。

0 0