本章小结
程序清单
友元类
15.1 tv.h 15.2 tv.cpp 15.3 use_tv.cpp 15.4 tvfm.h
嵌套类
15.5 queuetp.h 15.6 nested.cpp
异常
15.7 error1.cpp 15.8 error2.cpp 15.9 error3.cpp 15.10 exc_mean.h
15.11 error4.cpp 15.12 error5.cpp 15.13 newexcp.cpp 15.14 sales.h
15.15 sales.cpp 15.16 usesales.cpp
运行阶段类型识别(RTTI)
15.17 rtti1.cpp 15.18 rtti2.cpp
类型转换运算符
15.19 constcast.cpp
友元使得能够为类开发更灵活的接口。类可以将其他函数、其他类和其他类的成员函数作为友元。在某些情况下,可能需要使用前向声明,需要特别注意类和方法声明的顺序,以正确地组合友元。
嵌套类是在其他类中声明的类,它有助于设计这样的助手类,即实现其他类,但不必是公有接口的组成部分。
C++异常机制为处理拙劣的编程事件,如不适当的值、I/O失败等。提供了一种灵活的方式。引发异常将终止当前执行的函数,将控制权传给匹配的catch块。catch块紧跟在try块的后面,为捕获异常,直接或间接导致异常的函数调用必须位于try块中。这样程序将执行catch块中的代码。这些代码试图解决问题或终止程。类可以包含嵌套的异常类,嵌套异常类在相应的问题被发现时将被引发。 函数可以包含异常规范,指出在该函数中可能引发的异常:但C++11摒弃了这项功能。未被捕获的异常(没有匹配的catch块的异常)在默认情况下将终止程序,意外异常(不与任何异常规范匹配的异常)也是如此。
RTTI (运行阶段类型信息)特性让程序能够检测对象的类型。dynamic_cast运算符用于将派生类指针转换为基类指针,其主要用途是确保可以安全地调用虚函数。Typeid运算符返回一个type_info对象.可以 对两个typeid的返回值进行比较,以确定对象是否为特定的类型,而返回的type_info对象可用于获得关于对象的信息。
与通用转换机制相比,dynamic_cast、static_cast、const_cast 和 reinterpret_cast 提供了更安全、更明确的类型转换.
// tv.h -- Tv and Remote classes
#ifndef TV_H_
#define TV_H_
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 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
{
private:
int mode; // controls TV or DVD
public:
Remote(int m = Tv::TV) : mode(m) {}
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(); }
};
#endif
// tv.cpp -- methods for the Tv class (Remote methods are inline)
#include
#include "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::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 -- using the Tv and Remote classes
#include
#include "tv.h"
int main()
{
using std::cout;
Tv s42;
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.volup(s42);
cout << "\n42\" settings after using remote:\n";
s42.settings();
Tv s58(Tv::On);
s58.set_mode();
grey.set_chan(s58, 28);
cout << "\nS8\" settings:\n";
s58.settings();
return 0;
}
//tvfm.h -- Tv and Remote classes using a friend member
#ifndef TVFM_H_
#define TVFM_H_
class Tv;
class Remote
{
public:
enum { Off, On };
enum { MinVal, MaxVal = 20 };
enum { Antenna, Cable };
enum { TV, DVD };
Remote(int m = TV) : mode(m) {}
bool volup(Tv & t);
bool voldown(Tv & t);
void onoff(Tv & t);
void chanup(Tv & t);
void chandown(Tv & t);
void set_mode(Tv & t);
void set_input(Tv & t);
void set_chan(Tv & t, int c);
private:
int mode;
};
class Tv
{
public:
friend void Remote::set_chan(Tv & t, int c);//just this function 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 settings() const; // display all settings
private:
int state;
int volume;
int maxchannel;
int channel;
int mode;
int input;
};
// Remote methods as-inline functions
inline bool Remote::volup(Tv & t) { return t.volup(); }
inline bool Remote::voldown(Tv & t) { return t.voldown(); }
inline void Remote::onoff(Tv &t) { t.onoff(); }
inline void Remote::chanup(Tv & t) {t.chanup();}
inline void Remote::chandown(Tv & t) {t.chandown();}
inline void Remote::set_mode(Tv & t) {t.set_mode();}
inline void Remote::set_input(Tv & t) {t.set_input();}
inline void Remote::set_chan(Tv & t, int c) { t.channel = c; }
#endif // !TVFM_H_
// queuetp.h -- queue template with a nested class
#ifndef QUEUETP_H_
#define QUEUETP_H_
template
class QueueTP
{
private:
enum { Q_SIZE = 10 };
// Node is a nested class definition
class Node
{
public:
Item item;
Node * next;
Node(const Item & i) :item(i), next(0) {}
};
Node * front; // pointer to front of Queue
Node * rear; // pointer to rear of Queue
int items; // current number of items in Queue
const int qsize; // maximum number of items in Queue
QueueTP(const QueueTP & q) : qsize(0) {}
QueueTP & operator= (const QueueTP & q) { return *this; }
public:
QueueTP(int qs = Q_SIZE);
~QueueTP();
bool isempty() const
{
return items == 0;
}
bool isfull() const
{
return items == qsize;
}
int queuecount() const
{
return items;
}
bool enqueue(const Item &item); // add item to end
bool dequeue(Item &item) ; // remove item from front
};
// QueueTP methods
template
QueueTP- ::QueueTP(int qs) : qsize(qs)
{
front = rear = 0;
items = 0;
}
template
QueueTP- ::~QueueTP()
{
Node * temp;
while (front != 0) // while queue is not yet empty
{
temp = front; // save address of front item
front = front->next;// reset pointer to next item
delete temp; // delete former front
}
}
// Add item to queue
template
bool QueueTP- ::enqueue(const Item & item)
{
if (isfull())
return false;
Node * add = new Node(item); // create node // on failure, new throws std::bad_alloc exception
items++;
if (front == 0) // if queue is empty,
front = add; // place item at front
else
rear->next = add; // else place at rear
rear = add;// have rear point to new node
return true;
}
// Place front item into item variable and remove from queue
template
bool QueueTP- ::dequeue(Item & item)
{
if (front == 0)
return false;
item = front->item;// set item to first item in queue
items--;
Node * temp = front; // save location of first item
front = front -> next; // reset front to next item
delete temp; // delete former first item
if (items == 0)
rear = 0;
return true;
}
#endif
// nested.cpp -- using a queue that has a nested class
#include
#include
#include "queuetp.h"
int main()
{
using std::string;
using std::cin;
using std::cout;
QueueTP cs(5);
string temp;
while (!cs.isfull())
{
cout << "Please enter your name. You will be served in the order of arrival.\nname: ";
getline(cin, temp);
cs.enqueue(temp);
}
cout << "The queue is full. Processing begins!\n";
while (!cs.isempty())
{
cs.dequeue(temp);
cout << "Now processing " << temp << "...\n";
}
return 0;
}
使用abort()函数
//error1.cpp -- using the abort() function
#include
#include
using namespace std;
double hmean(double a, double b);
int main()
{
double x, y, z;
cout << "Enter two numbers: ";
while (cin >> x >> y)
{
z = hmean(x, y);
cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl;
cout << "Enter next set of numbers : ";
}
cout << "Bye!\n";
return 0;
}
double hmean(double a, double b)
{
if (a == -b)
{
cout << "untenable arguments to hmeanU\n";
abort();
}
return 2.0 * a * b / (a + b);
}
返回错误码
//error2.cpp -- returning an error code
#include
#include // (or float.h) for DBL_MAX
using namespace std;
bool hmean(double a, double b, double * ans);
int main()
{
double x, y, z;
cout << "Enter two numbers: ";
while (cin >> x >> y)
{
if (hmean(x, y, &z))
cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl;
else
cout << "One value should not be the negative " << "of the other - try again.\n";
cout << "Enter next set of numbers : ";
}
std::cout << "Bye!\n";
return 0;
}
bool hmean(double a, double b, double * ans)
{
if (a == -b)
{
*ans = DBL_MAX;
return false;
}
else
{
*ans = 2.0 * a * b / (a + b);
return true;
}
}
异常机制
//error3.cpp -- using an exception
#include
using namespace std;
double hmean(double a, double b);
int main()
{
double x, y, z;
cout << "Enter two numbers: ";
while (cin >> x >> y)
{
try//start of try block
{
z = hmean(x, y);
}//end of try block
catch (const char * s)//start of exception handler
{
cout << s << endl;
cout << "Enter a new pair of numbers: ";
continue;
}//end of handler
cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl;
cout << "Enter next set of numbers : ";
}
cout << "Bye!\n";
return 0;
}
double hmean(double a, double b)
{
if (a == -b)
throw "bad hmean() arguments: a = -b not allowed";
return 2.0 * a * b / (a + b);
}
将对象用作异常类型
// excmean.h -- exception classes for hmean(), gmean()
#include
using namespace std;
class bad_hmean
{
private:
double vl;
double v2;
public:
bad_hmean(double a = 0, double b = 0) : vl(a), v2(b) {}
void mesg();
};
inline void bad_hmean::mesg()
{
cout << "hmean(" << vl << ", " << v2 << "): invalid arguments: a = -b\n";
}
class bad_gmean
{
public:
double vl;
double v2;
bad_gmean(double a = 0, double b = 0) : vl(a), v2(b) {}
const char * mesg();
};
inline const char * bad_gmean::mesg()
{
return "gmean() arguments should b => 0\n";
}
//error4.cpp - using exception classes
#include
#include
#include "exc_mean.h"
using namespace std;
// function prototypes
double hmean(double a, double b) ;
double gmean(double a, double b);
int main()
{
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 : ";
}// end of try block
catch (bad_hmean & bg) // start of catch block
{
bg.mesg();
cout << "Try again.\n";
continue;
}
catch (bad_gmean & hg)
{
cout << hg.mesg();
cout << "Values used: " << hg.vl << ", " << hg.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(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 sqrt(a * b);
}
栈解退
//error5.cpp -- unwinding the stack
#include
#include
#include
#include "exc_mean.h"
class demo
{
private:
string word;
public:
demo(const string & str)
{
word = str;
std::cout << "demo " << word << " created\n";
}
~demo()
{
std::cout << "demo " << word << " destroyed\n";
}
void show() const
{
std::cout << "demo " << word << " lives!\n";
}
};
// function prototypes
double hmean(double a, double b);
double gmean(double a, double b);
double means(double a, double b);
int main()
{
using std::cout;
using std::cin;
using std::endl;
double x, y, z;
{
demo d1("found in block in main() ");
cout << "Enter two numbers : ";
while (cin >> x >> y)
{
try// stare of try block
{
z = means(x, y);
cout << "The mean mean of " << x << " and " << y << "is " << z << endl;
cout << "Enter next pair: ";
} // end of try block
catch (bad_hmean & bg) // start of catch block
{
bg.mesg();
cout << "Try again.\n";
continue;
}
catch (bad_gmean & hg)
{
cout << hg.mesg();
cout << "Values used: " << hg.vl << ", " << hg.v2 << endl;
cout << "Sorry, you don't get to play any more.\n";
break;
} // end of catch block
}
d1.show();
}
cout << "Bye!\n";
cin.get();
cin.get();
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);
}
double means(double a, double b)
{
double am, hm, gm;
demo d2("found in means{)");
am = (a + b) / 2.0; // arithmetic mean
try
{
hm = hmean(a, b);
gm = gmean(a, b);
}
catch (bad_hmean & bg) // start of catch block
{
bg.mesg();
std::cout << "Caught in means()\n";
throw;
}
d2.show();
return (am + hm + gm) / 3.0;
}
bad_alloc 异常和new
// newexcp.cpp -- the bad_alloc exception
#include
#include
#include // for exitO, EXIT FAILURE
using namespace std;
struct Big
{
double stuff[20000];
};
int main()
{
Big * pb;
try
{
cout << "Trying to get a big block of memory:\n";
pb = new Big[10000]; // 1,600,000,000 bytes
cout << "Got past the new request:\n";
}
catch (bad_alloc & ba)
{
cout << "Caught the exception!\n";
cout << ba.what() << endl;
exit(EXIT_FAILURE);
}
cout << "Memory successfully allocated\n";
pb[0].stuff[0] = 4;
cout << pb[0].stuff[0] << endl;
delete[] pb;
return 0;
}
异常、类和继承
// sales.h -- exceptions and inheritance
#include
#include
class Sales
{
public:
enum { MONTHS = 12 }; // could be a static const
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 implementation
#include "sales.h"
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 > n and i < MONTHS
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);
}
// usesales.cpp -- nested exceptions
#include
#include "sales.h"
int main()
{
using std::cout;
using std::cin;
using std::endl;
double vals1[12] =
{
1220, 1100, 1122, 2212, 1232, 2334,
2864, 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 l.\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();
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();
cout << "bad index: " << bad.bi_val() << endl;
}
cout << "done\n";
return 0;
}
dynamic_cast运算符
//rttil.cpp -- using the RTTI dynamic_cast operator
#include
#include
#include
using std::cout;
class Grand
{
private:
int hold;
public:
Grand(int h = 0) : hold(h) {}
virtual void Speak() const { cout << "I am a grand class h\n"; }
virtual int Value() const { return hold; }
};
class Superb : public Grand
{
public:
Superb(int h = 0) : Grand(h) {}
void Speak() const { cout << "I am a superb class!!\n"; }
virtual void Say() const
{
cout << "I hold the superb value of: " << Value() << "!\n";
}
};
class Magnificent : public Superb
{
private:
char ch;
public:
Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
void Speak() const { cout << "I am a magnificent class I !!\n"; }
void Say() const { cout << "I hold the character " << ch << " and the integer " << Value() << "!\n"; }
};
Grand * GetOne();
int main()
{
std::srand(std::time(0));
Grand * pg;
Superb * ps;
for (int i = 0; i < 5; i++)
{
pg - GetOne();
pg->Speak();
if (ps = dynamic_cast(pg))
ps->Say();
}
return 0;
}
Grand * GetOne() // generate one of three kinds of objects randomly
{
Grand * p;
switch(std::rand() % 3)
{
case 0: p = new Grand(std::rand() % 100);
break;
case 1: p = new Superb(std::rand() % 100);
break;
case 2: p = new Magnificent(std::rand() % 100, 'A' + std::rand() % 26);
break;
}
return p;
}
typeid运算符和type_info类
// rtti2.cpp -- using dynamic_cast, typeid, and type_info
#include
#include
#include
#include
using namespace std;
class Grand
{
private:
int hold;
public:
Grand(int h = 0) : hold(h) {}
virtual void Speak() const { cout << "I am a grand class!\n"; }
virtual int Value() const { return hold; }
};
class Superb : public Grand
{
public:
Superb( int h = 0) : Grand(h) {}
void Speak() const { cout << "I am a superb class!!\n"; }
virtual void Say() const { cout << "I hold the superb value of " << Value() << "!\n"; }
};
class Magnificent : public Superb
{
private:
char ch;
public:
Magnificent(int h = 0, char cv = 'A') : Superb(h), ch(cv) {}
void Speak() const { cout << "I am a magnificent class!!!\n"; }
void Say() const { cout << "I hold the character " << ch << "and the integer " << Value() << "!\n"; }
};
Grand * GetOne();
int main()
{
srand(time(0));
Grand * pg;
Superb * ps;
for (int i = 0 ; i < 5; i++)
{
pg = GetOne();
cout << "Now processing type " << typeid(*pg).name() << "\n";
pg -> Speak();
if (ps = dynamic_cast(pg))
ps->Say();
if (typeid(Magnificent) == typeid(*pg))
cout << "Yes, you're really magnificent.\n";
}
return 0;
}
Grand * GetOne()
{
Grand * p;
switch (rand() % 3)
{
case 0: p = new Grand(rand() % 100);
break;
case 1: p = new Superb(rand() % 100);
break;
case 2: p = new Magnificent(rand() % 100, 'A' + rand() % 26);
break;
}
return p;
}
使用const_cast运算符
// constcast.cpp -- using constcasto
#include
using std::cout;
using std::endl;
void change(const int * pt, int n);
int main()
{
int pop1 = 38383;
const int pop2 = 2000;
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
change(&pop1, -103);
change(&pop2, -103);
cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
return 0;
}
void change(const int * pt, int n)
{
int * pc;
pc = const_cast(pt);
*pc += n;
}