第十五章:友元、异常和其他
一、复习题
1. a. 友元声明应为: friend class clasp
b. 应该在cuff类前声明muff类
c. 应该在muff类前声明cuff类。其次,需要muff的前向声明。
2. 不可以。为使类A拥有一个本身为类B的成员函数的友元,B的声明必须位于 A 的声明之前。一个前向声明是不够的,因为这种声明可以告诉A:B是一个类;但它不能指出类成员的名称。同样,如果B拥有一个本身是A 的成员函数的友元,则A 的这个声明必须位于B的声明之前。这两个要求是互斥的。
3. 嵌套类被定义为私有成员,意味着在ribs类外无法创建嵌套类的对象。仅能通过ribs类的构造函数创建该嵌套类的一个对象。
4. return会依次回调先前调用该函数的函数,但throw会跳转到相应的含有try块的调用函数中进行处理。
5. catch块的顺序应该与继承层次相反,即基类在下,派生类在上。
6. 第一个实例中,可以调用superb中的say方法以及superb派生类magnificent中的say方法。在第二个实例中,仅能调用superb中的say方法。
7. static_cast运算符能在类层次中进行向上或向下的转换,但dynamic_cast运算符仅能进行向上的转换。static_cast还允许枚举类型和整型之间以及数值类型之间的转换。
二、编程练习
1.
#ifndef C_PRIMER_CHAPTER15_TV_H
#define C_PRIMER_CHAPTER15_TV_H
class Remote;
class Tv
{
private:
int state;
int volume;
int maxChannel;
int channel;
int input;
public:
friend class Remote;
enum {Off,On};
enum {MinVal,MaxVal = 20};
enum {TV,DVD};
Tv(int s = Off, int mc = 125):state(s),volume(5),
maxChannel(mc),channel(2),input(TV){}
void onOff() {state = state == Off ? On : Off;}
bool isOn() {return state;}
bool volUp();
bool volDown();
void chanUp();
void chanDown();
void set_input() {input = input == TV ? DVD : TV;}
void settings() const;
void swicthRemote(Remote& r);
};
class Remote
{
private:
int mode;
int r_state;
public:
friend class Tv;
enum {Normal,Interactive};
Remote(int m = Tv::TV,int s = Normal) : mode(m),r_state(s){}
void onOff(Tv& t) {t.onOff();}
void volUp(Tv& t) {t.volUp();}
void volDown(Tv& t) {t.volDown();}
void chanUp(Tv& t) {t.chanUp();}
void chanDown(Tv& t) {t.chanDown();}
void set_chan(Tv& t,int c){t.channel = c;}
void set_input(Tv& t) {t.set_input();}
void settings() const;
};
#endif //C_PRIMER_CHAPTER15_TV_H
#include "Tv.h"
#include <iostream>
bool Tv::volUp()
{
if(volume < MaxVal)
{
volume++;
return true;
}
return false;
}
bool Tv::volDown()
{
if(volume <= MinVal)
return false;
volume--;
return true;
}
void Tv::chanUp()
{
if(channel == maxChannel)
channel = 0;
else
channel++;
}
void Tv::chanDown()
{
if(channel == 0)
channel = maxChannel;
else
channel--;
}
void Tv::settings() const
{
using std::cout,std::endl;
if(state == On)
{
cout << "Tv is On." << endl;
cout << "Volume: " << volume << endl;
cout << "Channel: " << channel << endl;
cout << "Input: " << (input ? "DVD" : "Tv") << endl;
}
else
{
cout << "Tv is Off." << endl;
}
}
void Tv::swicthRemote(Remote& r)
{
if(state == On)
{
r.r_state = r.r_state == Remote::Normal ? Remote::Interactive : Remote::Normal;
}
else
{
std::cout << "Tv is Off." << std::endl;
}
}
void Remote::settings() const
{
using std::cout,std::endl;
cout << "Mode: " << (mode ? "DVD" : "TV") << endl;
cout << "State: " << (r_state ? "Interactive" : "Normal") << endl;
}
#include "tv.h"
int main() {
Tv t;
t.settings();
Remote r;
r.settings();
r.onOff(t);
t.settings();
r.volDown(t);
t.settings();
t.swicthRemote(r);
r.settings();
return 0;
}
2.
#include <iostream>
#include <stdexcept>
#include <cmath>
class bad_hmean : public std::logic_error
{
public:
bad_hmean(const std::string& what_arg = "Invalid for hmean") : std::logic_error(what_arg){}
};
class bad_gmean : public std::logic_error
{
public:
bad_gmean(const std::string& what_arg = "Invalid for gmean") : std::logic_error(what_arg){}
};
double hmean(double , double );
double gmean(double , double );
int main()
{
double x,y;
std::cout << "Enter two numbers: ";
while(std::cin >> x >> y) {
try
{
std::cout << hmean(x,y) << '\n';
std::cout << gmean(x,y) << '\n';
std::cout << "Enter two numbers: ";
}
catch (std::exception& e)
{
std::cout << e.what();
std::cout << "\nEnter two numbers: ";
}
}
return 0;
}
double hmean(double x, double y)
{
if(x == -y)
throw bad_hmean();
return 2.0 * x * y / (x + y);
}
double gmean(double x, double y)
{
if(x < 0 || y < 0)
throw bad_gmean();
return sqrt(x * y);
}
3.
#include <iostream>
#include <stdexcept>
#include <cmath>
class bad : public std::logic_error
{
private:
double m_x;
double m_y;
public:
bad(double x, double y, const std::string& what_arg = "Invalid arguments\n") : m_x(x),m_y(y),std::logic_error(what_arg){}
void reports()
{
if(m_x == -m_y)
std::cout << "Error in hmean with x = " << m_x << " and y = " << m_y << std::endl;
else
std::cout << "Error in gmean with x = " << m_x << " and y = " << m_y << std::endl;
}
};
double hmean(double , double );
double gmean(double , double );
int main()
{
double x,y;
std::cout << "Enter two numbers: ";
while(std::cin >> x >> y) {
try
{
std::cout << hmean(x,y) << '\n';
std::cout << gmean(x,y) << '\n';
std::cout << "Enter two numbers: ";
}
catch (bad& e)
{
std::cout << e.what();
e.reports();
break;
}
}
return 0;
}
double hmean(double x, double y)
{
if(x == -y)
throw bad(x,y);
return 2.0 * x * y / (x + y);
}
double gmean(double x, double y)
{
if(x < 0 || y < 0)
throw bad(x,y);
return sqrt(x * y);
}
4.
#ifndef C_PRIMER_CHAPTER15_SALES_H
#define C_PRIMER_CHAPTER15_SALES_H
#include <stdexcept>
#include <string>
class Sales
{
public:
enum {MONTHS = 12};
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")
: std::logic_error(s),bi(ix){}
int bi_val() {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:
int year;
double gross[MONTHS];
};
class LabeledSales : public Sales
{
private:
std::string label;
public:
class nbad_index : public 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")
: bad_index(ix,s),lbl(lb){}
const std::string & label_val() {return lbl;}
virtual ~nbad_index(){}
};
explicit LabeledSales(const std::string & lb = "none", int yy = 0)
: Sales(yy),label(lb){}
LabeledSales(const std::string & lb, int yy, const double* gr, int n)
: Sales(yy,gr,n),label(lb){}
virtual ~LabeledSales(){}
const std::string & Label() const {return label;}
virtual double operator[](int i) const;
virtual double & operator[](int i);
};
#endif //C_PRIMER_CHAPTER15_SALES_H
#include "Sales.h"
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];
}
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);
}
#include <iostream>
#include "Sales.h"
int main()
{
using std::cout,std::endl,std::cin;
using std::cin;
using std::cout;
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);
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(Sales::bad_index& bad)
{
cout << bad.what();
cout << "Index: " << bad.bi_val() << endl;
Sales::bad_index* pbad = &bad;
LabeledSales::nbad_index* pb;
if((pb = dynamic_cast<LabeledSales::nbad_index*>(pbad)))
{
cout << "Company: " << pb->label_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 &bad)
{
cout << bad.what();
cout << "Index: " << bad.bi_val() << endl;
if (typeid(bad) == typeid(LabeledSales::nbad_index))
cout << "Company: " << ((LabeledSales::nbad_index &)bad).label_val() << endl;
}
cout << "done\n";
return 0;
}