C++ Primer Plus章节编程练习第十五章

1

题目

  对Tv和Remote类做如下修改:
  a.让它们互为友元;
  b.在Remote类中添加一个状态变量成员,该成员描述遥控器是处于常规模式还是互动模式:
  c.在Remote中添加一个显示模式的方法:
  d.在Tv类中添加一个对Remote中新成员进行切换的方法,该方法应仅当TV处于打开状态时才能运行。
  编写一个小程序来测试这些新特性。

题解

#include<iostream>
using namespace std;
//前向声明Tv
class Tv;
//Remote的声明
class Remote{
public:
    enum State{Off,On};
    enum{MinVal,MaxVal=20};
    enum{Antenna,Cable};
    enum{TV,DVD};
    enum{Interactive,Regular};          //遥控模式
private:
    int mode;
    int remote_mode;                    //遥控模式
public:
    friend class Tv;                    //TV是Remote的友元
    Remote(int m=TV,int rm=Regular):mode(m),remote_mode(rm){}
    bool volup(Tv &t);
    bool voldown(Tv &t);
    void onoff(Tv &t);
    void chanup(Tv &t);
    void chandown(Tv &t);
    void set_chan(Tv &t,int c);
    void set_mode(Tv &t);
    void set_input(Tv &t);
    void show()const;
};
class Tv{
private:
    int state;
    int volume;
    int maxchannel;
    int channel;
    int mode;
    int input;
public:
    friend class Remote;                //Remote是Tv的友元,Remote可以访问Tv的成员
    enum State{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 settings()const;
    void change_remote_mode(Remote &rt);
};
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::settings()const{
    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;
    }
}
void Tv::change_remote_mode(Remote &rt){
    if(state==On)
        rt.remote_mode=(rt.remote_mode==Remote::Regular?Remote::Interactive:Remote::Regular);
}

//Remote的方法定义,因为前置声明不知道tv的内容,所以remote的方法只声明,放在直到tv的定义之后在定义
bool Remote::volup(Tv &t){return t.volup();}
bool Remote::voldown(Tv &t){return t.voldown();}
void Remote::onoff(Tv &t){t.onoff();}
void Remote::chanup(Tv &t){t.chanup();}
void Remote::chandown(Tv &t){t.chandown();}
void Remote::set_chan(Tv &t,int c){t.channel=c;}
void Remote::set_mode(Tv &t){t.set_mode();}
void Remote::set_input(Tv &t){t.set_input();}
void Remote::show()const{
    cout<<"Mode: "<<(mode==TV?"TV":"DVD")<<endl;
    cout<<"Remote Mode: "
        <<(remote_mode==Regular?"Regular":"Interactive")<<endl;
}

//测试函数
int main(){
    Tv s42;                                             //测试TV正常工作
    cout<<"Initial settings for 42\" TV:\n";
    s42.settings();
    s42.onoff();
    s42.chanup();
    cout<<"\nAdjusted settings for 42\" TV:\n";
    s42.settings();

    Remote grey;                                        //测试Remote正常工作
    grey.set_chan(s42,10);
    grey.volup(s42);
    grey.volup(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();

    cout<<endl;
    cout<<"Remote grey's setting:"<<endl;
    grey.show();
    s42.change_remote_mode(grey);
    cout<<"after s42 changes the remote_mode of grey:"<<endl;
    grey.show();
    s58.onoff();                    //关闭s58,则切换动作不会完成
    s58.change_remote_mode(grey);
    cout<<"s58 is Off!"<<endl;
    cout<<"after s58 changes the remote_mode of grey:"<<endl;
    grey.show();
    return 0;
}

2

题目

  修改程序清单15.11,使两种异常类型都是从头文件提供的logic_error类派生出来的类。让每个what()方法都报告函数名和问题的性质。异常对象不用存储错误的参数值,而只需支持what()方法。

题解

#include<iostream>
#include<cmath>
#include<string>
#include<stdexcept>
//异常类的声明和定义
//logic_error没有默认构造函数,所以直接构造即可
class bad_hmean:public std::logic_error{
public:
    bad_hmean():std::logic_error("Except from hmean() with invalid arguments: a=-b\n"){}
    std::string what(){return std::logic_error::what();}
};

class bad_gmean:public std::logic_error{
public:
    double v1;
    double v2;
    bad_gmean():std::logic_error("Except from gmean() with invalid arguments: arguments shoulde be >=0\n"){}
    std::string what(){return std::logic_error::what();}
};

double hmean(double a,double b);
double gmean(double a,double b);
int main(){
    double x,y,z;
    std::cout<<"Enter two numbers: ";
    while(std::cin>>x>>y){
        try{
            z=hmean(x,y);               //如果这条语句将会触发异常,则catch会捕获异常
            std::cout<<"Harmonic mean of "<<x<<" and "<<y
                <<" is "<<z<<std::endl;
            std::cout<<"Geometric mean of "<<x<<" and "<<y
                <<" is "<<gmean(x,y)<<std::endl;
            std::cout<<"Enter next set of numbers <q to quit>: ";
        }
        catch(bad_hmean& bg){            //catch捕获的异常类型bad_hmean
            std::cout<<bg.what();
            std::cout<<"Try again.\n";
            continue;
        }
        catch(bad_gmean& hg){            //catch捕获的异常类型bad_gmean
            std::cout<<hg.what();
            std::cout<<"Sorry, you don't get to play any more.\n";
            break;
        }
    }
    std::cout<<"Bye!\n";
    return 0;
}
double hmean(double a,double b){
    if(a==-b)
        throw bad_hmean();
    return 2.0*a*b/(a+b);
}
double gmean(double a,double b){
    if(a<0||b<0)
        throw bad_gmean();
    return std::sqrt(a*b);
}

3

题目

  这个练习与编程练习2相同,但异常类是从一个这样的基类派生而来的:它是从logic_error派生而来的,并存储两个参数值。异常类应该有一个这样的方法:报告这些值以及函数名。程序使用一个catch块来捕获基类异常,其中任何一种从该基类异常派生而来的异常都将导致循环结束。

题解

#include<iostream>
#include<cmath>
//异常类的基类声明和定义
class bad_except:public std::logic_error{
protected:
    double v1,v2;
public:
    bad_except(double a=0,double b=0,std::string er="Exception!!!"):
        std::logic_error(er),v1(a),v2(b){}
    virtual std::string what(){return std::logic_error::what();}
};
//两个异常类的定义和实现
class bad_hmean:public bad_except{
public:
    bad_hmean(double a=0,double b=0,std::string er="Except from hmean() with invalid arguments: a=-b\n"):
        bad_except(a,b,er){}
    std::string what();
    void who()const{std::cout<<"From hmean(), and two value: "<<v1<<", "<<v2<<std::endl;}
};
std::string bad_hmean::what(){
    return bad_except::what();
}
class bad_gmean:public bad_except{
public:
    bad_gmean(double a=0,double b=0,std::string er="Except from hmean() with invalid arguments: a=-b\n"):
        bad_except(a,b,er){}
    std::string what();
    void who()const{std::cout<<"From gmean(), and two value: "<<v1<<", "<<v2<<std::endl;} 
};
std::string bad_gmean::what(){
    return bad_except::what();
}
double hmean(double a,double b);
double gmean(double a,double b);
int main(){
    double x,y,z;
    std::cout<<"Enter two numbers: ";
    while(std::cin>>x>>y){
        try{
            z=hmean(x,y);               //如果这条语句将会触发异常,则catch会捕获异常
            std::cout<<"Harmonic mean of "<<x<<" and "<<y
                <<" is "<<z<<std::endl;
            std::cout<<"Geometric mean of "<<x<<" and "<<y
                <<" is "<<gmean(x,y)<<std::endl;
            std::cout<<"Enter next set of numbers <q to quit>: ";
        }
        catch(bad_hmean& bg){            //catch捕获的异常类型bad_hmean
            std::cout<<bg.what();
            bg.who();
            break;
        }
        catch(bad_gmean& hg){            //catch捕获的异常类型bad_gmean
            std::cout<<hg.what();
            hg.who();
            break;
        }
    }
    std::cout<<"Bye!\n";
    return 0;
}
double hmean(double a,double b){
    if(a==-b)
        throw bad_hmean(a,b);
    return 2.0*a*b/(a+b);
}
double gmean(double a,double b){
    if(a<0||b<0)
        throw bad_gmean(a,b);
    return std::sqrt(a*b);
}

4

题目

  程序清单15.16在每个try后面都使用两个catch块,以确保nbad_index异常导致方法label_val()被调用。请修改该程序,在每个try块后面只使用一个catch块,并使用RTTI来确保合适时调用label_val()。

题解

#include<stdexcept>
#include<string>
#include<iostream>
class Sales{
public:
    enum{MONTHS=12};
private:
    double gross[MONTHS];
    int year;
public:
    class bad_index:public std::logic_error{
    private:
        int bi;
    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);
};
Sales::bad_index::bad_index(int ix,const std::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];
}

class LabeledSales:public Sales{
private:
    std::string label;
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);
};
LabeledSales::nbad_index::nbad_index(const std::string&lb,int ix,const std::string&s):
                                    Sales::bad_index(ix,s){
    lbl=lb;
}
LabeledSales::LabeledSales(const std::string& lb,int yy):Sales(yy){
    label=lb;
}
LabeledSales::LabeledSales(const std::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);
}

//测试函数
int main(){
    using namespace std;
    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";
    }
    //bad由nbad派生而来,基类引用可以指向派生类,派生类通过RTTI转为基类(要保证安全性)
    catch(Sales::bad_index& er){
        if(typeid(er)==typeid(LabeledSales::nbad_index&)){  //根据实际的类型进行转换
            cout<<dynamic_cast<LabeledSales::nbad_index&>(er).what();
            cout<<"Company: "<<dynamic_cast<LabeledSales::nbad_index&>(er).label_val()<<endl;
            cout<<"bad index: "<<dynamic_cast<LabeledSales::nbad_index&>(er).bi_val()<<endl;
        }
        if(typeid(er)==typeid(Sales::bad_index&)){
            cout<<er.what();
            cout<<"bad index: "<<er.bi_val()<<endl;
        }
    }

    cout<<"\nNext try block:\n";
    try{
        sales2[2]=37.5;
        sales1[20]=23345;
        cout<<"End of try block 2.\n";
    }
    catch(Sales::bad_index& er){
        if(typeid(er)==typeid(LabeledSales::nbad_index&)){
            cout<<dynamic_cast<LabeledSales::nbad_index&>(er).what();
            cout<<"Company: "<<dynamic_cast<LabeledSales::nbad_index&>(er).label_val()<<endl;            //这个问题还未解决
            cout<<"bad index: "<<dynamic_cast<LabeledSales::nbad_index&>(er).bi_val()<<endl;
        }
        if(typeid(er)==typeid(Sales::bad_index&)){
            cout<<er.what();
            cout<<"bad index: "<<er.bi_val()<<endl;
        }
    }
    cout<<"done\n";
    return 0;
}

当珍惜每一片时光~