《C++ primer plus》第15章:友元、异常和其他(9)
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
编程练习
-
对Tv 和 Remote 类做如下修改
a. 让它们互为友元
b. 在 Remote 类中添加一个状态变量成员该成员描述遥控器是出于常规模式还是互动模式
c. 在 Remote 中添加一个显式模式的方法
d. 在 Tv 类中添加一个对 Remote 中新成员进行切换的方法该方法应仅当 TV 处打开状态时才能运行。
编写一个小程序来测试这些新特性。
答// tv.h -- Tv and Remote classes #ifndef TV_H_ #define TV_H_ #include<iostream> class Remote; class Tv{ public: friend class Remote; // Remote can access Tv private parts enum {Off, On}; enum {MinVal, MaxVal = 20}; enum {Antenna, Cable}; enum {TV, DVD}; Tv(int s = Off, int mc = 125) : state(s), volume(5), maxchannel(mc), channel(2), mode(Cable), input(TV) {} void onoff() {state = (state == On) ? Off : On;} bool ison() const { return state == On; } bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode() { mode = (mode==Antenna) ? Cable : Antenna; } void set_input() { input = (input == TV)? DVD : TV;} void set_remote_status(Remote & r); void settings() const; // display all settings private: int state; // on or off int volume; // assumed to be digitized int maxchannel; // maximum number of channels int channel; // current channel setting int mode; // broadcast or cable int input; // TV or DVD }; class Remote{ public: friend class Tv; enum { NORMAL, INTERRANCTIVE }; private: int mode; // controls TV or DVD int status; // controls NORMAL or INTERACTIVE public: Remote(int m = Tv::TV, int s = Remote::NORMAL) : mode(m),status(s) { } bool volup(Tv & t) { return t.volup(); } bool voldown(Tv & t) { return t.voldown(); } void onoff(Tv & t) { t.onoff(); } void chanup(Tv & t) { t.chanup(); } void chandown(Tv & t) { t.chandown(); } void set_chan(Tv & t, int c) { t.channel = c; } void set_mode(Tv & t) { t.set_mode(); } void set_input(Tv & t) { t.set_input(); } void show_status() { std::cout<< "status: "; if(status == NORMAL) std::cout << "NORMAL"; else std::cout << "INTERACTIVE"; std::cout << std::endl; } }; #endif
tv.cpp
// tv.cpp -- methods for the Tv class (Remote methods are inline) #include <iostream> #include "15-1_tv.h" bool Tv::volup(){ if (volume < MaxVal){ volume++; return true; } else{ return false; } } bool Tv::voldown(){ if(volume > MinVal){ volume--; return true; } else { return false; } } void Tv::chanup(){ if (channel < maxchannel) channel++; else channel = 1; } void Tv::chandown(){ if (channel > 1) channel--; else channel = maxchannel; } void Tv::set_remote_status(Remote & r){ if(state == On){ if(r.status == Remote::NORMAL){ r.status = Remote::INTERRANCTIVE; } else{ r.status = Remote::NORMAL; } } } void Tv::settings() const{ using std::cout; using std::endl; cout << "TV is " << (state == Off? "Off" : "On") << endl; if(state == On){ cout << "Volume setting = " << volume << endl; cout << "Channel setting = " << channel << endl; cout << "Mode = " << (mode == Antenna? "antenna" : "cable") << endl; cout << "Input = " << (input == TV? "TV" : "DVD") << endl; } }
use_tv.cpp
#include"15-1_tv.h" int main(){ Tv s42; using std::cout; cout << "Initial settings for 42\" TV:\n"; s42.settings(); s42.onoff(); s42.chanup(); cout << "\nAdjusted settings for 42\" TV:\n"; s42.chanup(); cout << "\nAdjusted settings for 42\" TV:\n"; s42.settings(); Remote grey; grey.set_chan(s42, 10); grey.volup(s42); grey.voldown(s42); cout << "\n42\" settings after using remote:\n"; s42.settings(); Tv s58(Tv::On); s58.set_mode(); grey.set_chan(s58, 28); cout << "\n58\" settings:\n"; s58.settings(); grey.show_status(); s58.set_remote_status(grey); grey.show_status(); }
-
修改程序清单 15.11使两种异常类型都是从头文件<stdexcept>提供的 logic_error 类派生出来的类。让每个 what() 方法都报告函数名和问题的性质。异常对象不用存储错误的参数值而只需支持 what() 方法。
exc_mean.h#include<iostream> #include<stdexcept> class bad_hmean : public std::logic_error{ public: bad_hmean( const char * s) : logic_error(s) {} //void mesg(); }; // inline void bad_hmean::mesg(){ // std::cout << "hmean(" << v1 << ", " << v2 << "): " // << "invalid arguments: a = -b\n"; // } class bad_gmean: public std::logic_error{ public: // double v1; // double v2; bad_gmean(const char * s) : logic_error(s){} //const char * mesg(); }; // inline const char * bad_gmean::mesg(){ // return "gmean() arguments should be >= 0\n"; // }
error.cpp
// error4.cpp -- using exception classes #include<iostream> #include<cmath> // or math.h, unix users may need -lm flag #include"15-2_exc_mean.h" // function prototypes double hmean(double a, double b); double gmean(double a, double b); int main(){ using std::cout; using std::cin; using std::endl; double x, y, z; cout << "Enter two numbers: "; while(cin >> x >> y){ try { // start of try block z = hmean(x, y); cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl; cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x, y) << endl; cout << "Enter next set of numbers < q to quit>: "; } // end of try block catch(bad_hmean & bh){ // start of catch block //bh.mesg(); cout << bh.what() << endl;; cout << "Try again.\n"; continue; } catch(bad_gmean & bg){ cout << bg.what() << endl;; // cout << bg.mesg(); // cout << "Values used: " << bg.v1 << ", " // << bg.v2 << endl; cout << "Sorry, you don't get to play any more.\n"; break; } // end of catch block } cout << "Bye!\n"; return 0; } double hmean(double a, double b){ if (a==-b){ throw bad_hmean("hmean: invalid arguments -- a = -b."); } return 2.0 * a * b / (a + b); } double gmean(double a, double b){ if (a < 0 || b < 0){ throw bad_gmean("gmean: arguments shold be >= 0."); } return std::sqrt(a * b); }
-
这个练习与练习2相同但异常类是从这样的基类派生而来的它是从 logic_error 派生而来的并存储两个参数值。异常类应该有一个这样的方法报告这些值以及函数名。程序使用一个catch块来捕获基类异常其中任何一种从该基类异常派生而来的异常都将导致循环结束。
exc_mean.h#include<iostream> #include <stdexcept> class bad_mean : public std::logic_error{ public: double v1, v2; bad_mean(const char *s, double a = 0, double b = 0) : v1(a), v2(b), logic_error(s) {} virtual void mesg(){ std::cout << "func: " << what() << std::endl; std::cout << "arguments: " << v1 << ", " << v2 << ". \n"; } }; class bad_hmean : public bad_mean { public: bad_hmean(const char * s, double a = 0, double b = 0) : bad_mean(s,a,b) {} void mesg(); }; inline void bad_hmean::mesg(){ bad_mean::mesg(); std::cout << "invalid arguments: a = -b\n"; } class bad_gmean : public bad_mean{ public: bad_gmean(const char * s, double a = 0, double b = 0) : bad_mean(s,a,b) {} void mesg(); }; inline void bad_gmean::mesg(){ bad_mean::mesg(); std::cout << "gmean() arguments should be >= 0\n"; }
error.cpp
// error4.cpp -- using exception classes #include<iostream> #include<cmath> // or math.h, unix users may need -lm flag #include"15-3_exc_mean.h" // function prototypes double hmean(double a, double b); double gmean(double a, double b); int main(){ using std::cout; using std::cin; using std::endl; double x, y, z; cout << "Enter two numbers: "; while(cin >> x >> y){ try { // start of try block z = hmean(x, y); cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl; cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x, y) << endl; cout << "Enter next set of numbers < q to quit>: "; } // end of try block catch(bad_mean & bm){ // start of catch block bm.mesg(); cout << "Sorry, you don't get to play any more.\n"; break; } // end of catch block } cout << "Bye!\n"; return 0; } double hmean(double a, double b){ if (a==-b){ throw bad_hmean("hmean",a,b); } return 2.0 * a * b / (a + b); } double gmean(double a, double b){ if (a < 0 || b < 0){ throw bad_gmean("gmean",a,b); } return std::sqrt(a * b); }
-
程序清单15.16在每个 try 块后面都使用两个 catch 块以确保 nbad_index 异常导致方法 label_val() 被调用。请修改该程序在每个try块后面只使用一个 catch 块并使用 RTTI 来确保合适时调用 label_val()。
sales.h// sales.h -- exceptions and inheritance #include<stdexcept> #include<string> class Sales{ protected: enum {MONTHS = 12}; // could be a static const public: class bad_index : public std::logic_error{ private: int bi; // bad index value public: explicit bad_index(int ix, const std::string & s = "Index error in Sales object\n"); int bi_val() const { return bi; } virtual ~bad_index() throw() {} }; explicit Sales(int yy = 0); Sales(int yy, const double * gr, int n); virtual ~Sales() {} int Year() const { return year; } virtual double operator[](int i) const; virtual double & operator[](int i); private: double gross[MONTHS]; int year; }; class LabeledSales : public Sales{ public: class nbad_index : public Sales::bad_index{ private: std::string lbl; public: nbad_index(const std::string & lb, int ix, const std::string & s = "Index error in LabeledSales object\n"); const std::string & label_val() const { return lbl;} virtual ~nbad_index() throw() {} }; explicit LabeledSales(const std::string & lb = "none", int yy = 0); LabeledSales(const std::string & lb, int yy, const double * gr, int n); virtual ~LabeledSales() {} const std::string & Label() const { return label; } virtual double operator[](int i) const; virtual double & operator[](int i); private: std::string label; };
sales.cpp
// sales.cpp -- Sales implementation #include"15-4_sales.h" #include <stdexcept> using std::string; Sales::bad_index::bad_index(int ix, const string & s) : std::logic_error(s), bi(ix){ } Sales::Sales(int yy){ year = yy; for (int i = 0; i < MONTHS; ++i){ gross[i] = 0; } } Sales::Sales(int yy, const double * gr, int n){ year = yy; int lim = (n < MONTHS)? n : MONTHS; int i; for (i = 0; i < lim; ++i){ gross[i] = gr[i]; } for (; i < MONTHS; ++i){ gross[i] = 0; } } double Sales::operator[](int i) const{ if (i < 0 || i >= MONTHS) throw bad_index(i); return gross[i]; } double & Sales::operator[](int i){ if(i<0||i>=MONTHS){ throw bad_index(i); } return gross[i]; } LabeledSales::nbad_index::nbad_index(const string & lb, int ix, const string & s) : Sales::bad_index(ix, s){ lbl = lb; } LabeledSales::LabeledSales(const string & lb, int yy) : Sales(yy){ label = lb; } LabeledSales::LabeledSales(const string & lb, int yy, const double * gr, int n) : Sales(yy, gr, n){ label = lb; } double LabeledSales::operator[](int i) const{ if (i < 0 || i >= MONTHS){ throw nbad_index(Label(), i); } return Sales::operator[](i); } double & LabeledSales::operator[](int i){ if (i < 0 || i >= MONTHS){ throw nbad_index(Label(), i); } return Sales::operator[](i); }
use_sales.cpp
// use_sales.cpp -- nested exceptions #include<iostream> #include"15-4_sales.h" int main(){ using std::cout; using std::cin; using std::endl; double vals1[12] = { 1220, 1100, 1122, 2212, 1232, 2334, 2884, 2393, 3302, 2922, 3002, 3544 }; double vals2[12] = { 12, 11, 22, 21, 32, 34, 28, 29, 33, 29, 32, 35 }; Sales sales1(2011, vals1, 12); LabeledSales sales2("Blogstar", 2012, vals2, 12); cout << "First try block:\n"; try{ int i; cout << "Year = " << sales1.Year() << endl; for (i=0; i<12; i++){ cout << sales1[i] << ' '; if ( i % 6 == 5) cout << endl; } cout << "Year = " << sales2.Year() << endl; cout << "Label = " << sales2.Label() << endl; for (i=0; i<= 12; i++){ cout << sales2[i] << ' '; if (i % 6 == 5) cout << endl; } cout << "End of try block 1.\n"; } // catch(LabeledSales::nbad_index & bad){ // cout << bad.what(); // cout << "Company: " << bad.label_val() << endl; // cout << "bad index: " << bad.bi_val() << endl; // } catch(Sales::bad_index & bad){ cout << bad.what(); LabeledSales::nbad_index * nbad; if((nbad=dynamic_cast<LabeledSales::nbad_index *> (&bad))){ cout << "Company: " << nbad->label_val() << endl; } cout << "bad index: " << bad.bi_val() << endl; } cout << "\nNext try block:\n"; try{ sales2[2] = 37.5; sales1[20] = 23345; cout << "End of try block 2.\n"; } // catch(LabeledSales::nbad_index & bad){ // cout << bad.what(); // cout << "Company: " << bad.label_val() << endl; // cout << "bad index: " << bad.bi_val() << endl; // } catch(Sales::bad_index & bad){ cout << bad.what(); LabeledSales::nbad_index * nbad; if((nbad=dynamic_cast<LabeledSales::nbad_index *> (&bad))){ cout << "Company: " << nbad->label_val() << endl; } cout << "bad index: " << bad.bi_val() << endl; } cout << "done\n"; return 0; }