1. Trang chủ
  2. » Công Nghệ Thông Tin

C++ Primer Plus (P52) pot

20 234 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 47,25 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The class methods will create objects of this class if they throw exceptions of the BadIndex type.. Listing 15.12 arraydbe.h // arraydbe.h -- define array class with exceptions #ifndef A

Trang 1

hold the value of the bad index Note that the nested class declaration just describes the

class; it doesn't create objects The class methods will create objects of this class if they

throw exceptions of the BadIndex type Also note that the nested class is public This

allows the catch blocks to have access to the type Listing 15.12 shows the new header

file The rest of the definition, aside from changing the class name, is the same as the

definition of ArrayDb in Chapter 14 , except that it qualifies the method prototypes to

indicate which exceptions they can throw That is, it replaces

virtual double & operator[](int i);

with

virtual double & operator[](int i) throw(ArrayDbe::BadIndex &);

and so on (As with ordinary function arguments, it's usually better to pass references

instead of objects when throwing exceptions.)

Listing 15.12 arraydbe.h

// arraydbe.h define array class with exceptions

#ifndef ARRAYDBE_H_

#define ARRAYDBE_H_

#include <iostream>

using namespace std;

class ArrayDbE

{

private:

unsigned int size; // number of array elements

protected:

double * arr; // address of first element

public:

class BadIndex // exception class for indexing problems

{

private:

int badindex; // problematic index value

public:

Trang 2

BadIndex(int i) : badindex(i) {}

virtual void Report() const;

};

ArrayDbE(); // default constructor

// create an ArrayDbE of n elements, set each to val

ArrayDbE(unsigned int n, double val = 0.0);

// create an ArrayDbE of n elements, initialize to array pn

ArrayDbE(const double * pn, unsigned int n);

// copy constructor

ArrayDbE(const ArrayDbE & a);

virtual ~ArrayDbE(); // destructor

unsigned int ArSize() const; // returns array size

double Average() const; // return array average

// overloaded operators

// array indexing, allowing assignment

virtual double & operator[](int i) throw(ArrayDbE::BadIndex &);

// array indexing (no =)

virtual const double & operator[](int i) const throw(ArrayDbE::BadIndex &);

ArrayDbE & operator=(const ArrayDbE & a);

friend ostream & operator<<(ostream & os, const ArrayDbE & a);

} ;

#endif

Next, you must provide the class methods These are the same methods used in Chapter

12 with the addition of some exception throwing Because the overloaded [] operators

throw exceptions instead of calling the exit() function, the program no longer needs to

include the cstdlib file Listing 15.13 shows the result.

Listing 15.13 arraydbe.cpp

// arraydbe.cpp ArrayDbE class methods

#include <iostream>

using namespace std;

#include "arraydbe.h"

Trang 3

// BadIndex method

void ArrayDbE::BadIndex::Report() const

{

cerr << "Out of bounds index value: " << badindex << endl;

}

// ArrayDbE methods

// default constructor no arguments

ArrayDbE::ArrayDbE()

{

arr = NULL;

size = 0;

}

// constructs array of n elements, each set to val

ArrayDbE::ArrayDbE(unsigned int n, double val)

{

arr = new double[n];

size = n;

for (int i = 0; i < size; i++)

arr[i] = val;

}

// initialize ArrayDbE object to a non-class array

ArrayDbE::ArrayDbE(const double *pn, unsigned int n)

{

arr = new double[n];

size = n;

for (int i = 0; i < size; i++)

arr[i] = pn[i];

}

// initialize ArrayDbE object to another ArrayDbE object

ArrayDbE::ArrayDbE(const ArrayDbE & a)

{

size = a.size;

arr = new double[size];

Trang 4

for (int i = 0; i < size; i++)

arr[i] = a.arr[i];

}

ArrayDbE::~ArrayDbE()

{

delete [] arr;

}

double ArrayDbE::Average() const

{

double sum = 0;

int i;

int lim = ArSize();

for (i = 0; i < lim; i++)

sum += arr[i];

if (i > 0)

return sum / i;

else

{

cerr << "No entries in score array\n";

return 0;

}

}

// return array size

unsigned int ArrayDbE::ArSize() const

{

return size;

}

// let user access elements by index (assignment allowed)

double & ArrayDbE::operator[](int i) throw(ArrayDbE::BadIndex &)

{

// check index before continuing

if (i < 0 || i >= size)

throw BadIndex(i);

return arr[i];

Trang 5

// let user access elements by index (assignment disallowed)

const double & ArrayDbE::operator[](int i)const throw(ArrayDbE::BadIndex &)

{

// check index before continuing

if (i < 0 || i >= size)

throw BadIndex(i);

return arr[i];

}

// define class assignment

ArrayDbE & ArrayDbE::operator=(const ArrayDbE & a)

{

if (this == &a) // if object assigned to self,

return *this; // don't change anything

delete arr;

size = a.size;

arr = new double[size];

for (int i = 0; i < size; i++)

arr[i] = a.arr[i];

return *this;

}

// quick output, 5 values to a line

ostream & operator<<(ostream & os, const ArrayDbE & a)

{

int i;

for (i = 0; i < a.size; i++)

{

os << a.arr[i] << " ";

if (i % 5 == 4)

os << "\n";

}

if (i % 5 != 0)

os << "\n";

return os;

Trang 6

Note that the exceptions now are objects instead of strings Also note that these exception

throws use the exception class constructor to create and initialize the exception objects:

if (i < 0 || i >= size)

throw BadIndex(i); // create, initialize a BadIndex object

What about catching this kind of exception? The exceptions are objects, not character

strings, so the catch block has to reflect that fact Also, because the exception is a nested

type, the code needs to use the scope resolution operator.

try {

}

catch(ArrayDbE::BadIndex &) {

}

Listing 15.14 provides a short program demonstrating the process.

Listing 15.14 exceptar.cpp

// exceptar.cpp use the ArrayDbE class

// Compile with arraydbe.cpp

#include <iostream>

using namespace std;

#include "arraydbe.h"

const int Players = 5;

int main()

{

try {

ArrayDbE Team(Players);

cout << "Enter free-throw percentages for your 5 "

"top players as a decimal fraction:\n";

int player;

Trang 7

for (player = 0; player < Players; player++)

{

cout << "Player " << (player + 1) << ": % = ";

cin >> Team[player];

}

cout.precision(1);

cout.setf(ios_base::showpoint);

cout.setf(ios_base::fixed,ios_base::floatfield);

cout << "Recapitulating, here are the percentages:\n";

for (player = 0; player <= Players; player++)

cout << "Player #" << (player + 1) << ": "

<< 100.0 * Team[player] << "%\n";

} // end of try block

catch (ArrayDbE::BadIndex & bi) // start of handler

{

cout << "ArrayDbE exception:\n";

bi.Report();

} // end of handler

cout << "Bye!\n";

return 0;

}

Compatibility Note

Some compilers may not recognize the new form ios_base:: and will require ios:: instead.

Note the second for loop deliberately exceeds the array bounds, triggering an exception.

Here is a sample run:

Enter free-throw percentages for your 5 top players as a decimal fraction:

Player 1: % = 0.923

Player 2: % = 0.858

Player 3: % = 0.821

Player 4: % = 0.744

Trang 8

Player 5: % = 0.697

Recapitulating, here are the percentages:

Player #1: 92.3%

Player #2: 85.8%

Player #3: 82.1%

Player #4: 74.4%

Player #5: 69.7%

ArrayDbE exception:

Out of bounds index value: 5

Bye!

Because the loop is inside the try block, throwing the exception terminates the loop as

control passes to the second catch block following the try block.

By the way, remember that variables defined in a block, including a try block, are local to

the block For example, the variable player is undefined once program control passes

beyond the try block in Listing 15.15

Exceptions and Inheritance

Inheritance interacts with exceptions in a couple of ways First, if a class has publicly

nested exception classes, a derived class inherits those exception classes Second, you

can derive new exception classes from existing ones We'll look at both these possibilities

in the next example.

First, derive a LimitArE class from the ArrayDbE class The LimitArE class will allow for

array indexing to begin with values other than 0 This can be accomplished by storing a

value representing the starting index and by redefining the functions Internally, the array

indexing still will begin with 0 But if, say, you specify 1900 as the starting index, the

operator[]() method will translate an external index of 1908 to an internal index

1908—1900, or 8 Listing 15.15 shows the details.

The BadIndex exception declared in the ArrayDbE class stored the offending index

value With variable index limits, it would be nice if the exception also stored the correct

range for indices You can accomplish this by deriving a new exception class from

BadIndex:

Trang 9

class SonOfBad : public ArrayDbE::BadIndex

{

private:

int l_lim;

int u_lim;

public:

SonOfBad(int i, int l, int u) : BadIndex(i),

l_lim(l), u_lim(u) {}

void Report() const; // redefines Report()

} ;

You can nest the SonOfBad declaration in the LimitArE declaration Listing 15.15 shows

the result.

Listing 15.15 limarre.h

// limarre.h LimitArE class with exceptions

#ifndef LIMARRE_H_

#define LIMARRE_H_

#include "arraydbe.h"

class LimitArE : public ArrayDbE

{

public:

class SonOfBad : public ArrayDbE::BadIndex

{

private:

int l_lim;

int u_lim;

public:

SonOfBad(int i, int l, int u) : BadIndex(i),

l_lim(l), u_lim(u) {}

void Report() const;

} ;

Trang 10

unsigned int low_bnd; // new data member

protected:

// handle bounds checking

virtual void ok(int i) const throw(ArrayDbE::BadIndex &);

public:

// constructors

LimitArE() : ArrayDbE(), low_bnd(0) {}

LimitArE(unsigned int n, double val = 0.0)

: ArrayDbE(n,val), low_bnd(0) {}

LimitArE(unsigned int n, int lb, double val = 0.0)

: ArrayDbE(n, val), low_bnd(lb) {}

LimitArE(const double * pn, unsigned int n)

: ArrayDbE(pn, n), low_bnd(0) {}

LimitArE(const ArrayDbE & a) : ArrayDbE(a), low_bnd(0) {}

// new methods

void new_lb(int lb) { low_bnd = lb;} // reset lower bound

int lbound() { return low_bnd;} // return lower bound

int ubound() { return ArSize() + low_bnd - 1;} // upper bound

// redefined operators

double & operator[](int i) throw(ArrayDbE::BadIndex &);

const double & operator[](int i) const throw(ArrayDbE::BadIndex &);

} ;

#endif

This design moves index checking from the overloaded [] methods to an ok() method that's

called by the overloaded [] methods C++ requires that the throw specification for virtual

method redefined in the derived class match the throw specification in the base class.

However, a base class reference can refer to a derived object Therefore, although the

LimitArE::ok() method throws a SonOfBad exception, the throw specifiers can list the

ArrayDbe::BadIndex class Listing 15.16 shows the class methods.

Listing 15.16 limarre.cpp

// limarre.cpp

#include "limarre.h"

Trang 11

#include <iostream>

using namespace std;

// BadIndex method

void LimitArE::SonOfBad::Report() const

{

ArrayDbE::BadIndex::Report();

cerr << "Index should be in the range " << l_lim

<< " through " << u_lim << endl;

}

//limarre methods

// private method

// lower bound for array index is now low_bnd, and

// upper bound is now low_bnd + size - 1

void LimitArE::ok(int i) const throw(ArrayDbE::BadIndex &)

{

unsigned long size = ArSize();

if (i < low_bnd || i >= size + low_bnd)

throw SonOfBad(i, low_bnd, low_bnd + size - 1);

}

// redefined operators

double & LimitArE::operator[](int i) throw(ArrayDbE::BadIndex &)

{

ok(i);

return arr[i - low_bnd];

}

const double & LimitArE::operator[](int i) const throw(ArrayDbE::BadIndex &)

{

ok(i);

return arr[i - low_bnd];

}

}

Suppose you have a program with both ArrayDbE and LimitArE objects Then you would

Trang 12

want a try block that catches the two possible exceptions: BadIndex and SonOfBad You

can do that by following the try block with two consecutive catch blocks:

try {

LimitArE income(Years, FirstYear);

ArrayDbE busywork(Years);

} // end of try block

catch (LimitArE::SonOfBad & bi) // 1st handler

{

}

catch (ArrayDbE::BadIndex & bi) // 2nd handler

{

}

When there is a sequence of catch blocks, a program attempts to match a thrown

exception to the first catch block, then the second catch block, and so on As soon as

there's a match, the program executes that catch block Providing the code in the catch

block doesn't terminate the program or generate another throw; the program jumps to the

statement following the final catch block after completing any one catch block in the

sequence.

This particular sequence of catch blocks has an interesting property—a catch block with a

BadIndex reference can catch either a BadIndex exception or a SonOfBad exception

That's because a base class reference can refer to a derived object However, a catch

block with a SonOfBad reference can't catch a BadIndex object That's because a

derived object reference can't refer to a base class object without an explicit type cast This

state of affairs suggests placing the SonOfBad catch block above the BadIndex catch

block That way, the SonOfBad catch block will catch a SonOfBad exception while

passing a BadIndex exception on to the next catch block The program in Listing 15.17

illustrates this approach.

Listing 15.17 excptinh.cpp

// excptinh.cpp use the ArrayDbE and LimitArE classes

Trang 13

// Compile with arraydbe.cpp, limarre.cpp

#include <iostream>

using namespace std;

#include "arraydbe.h"

#include "limarre.h"

const int Years = 4;

const int FirstYear = 2001;

int main()

{

int year;

double total = 0;

try {

LimitArE income(Years, FirstYear);

ArrayDbE busywork(Years);

cout << "Enter your income for the last " << Years

<< " years:\n";

for (year = FirstYear; year < FirstYear + Years; year++)

{

cout << "Year " << year << ": $";

cin >> income[year];

busywork[year - FirstYear] = 0.2 * income[year];

}

cout.precision(2);

cout.setf(ios::showpoint);

cout.setf(ios::fixed,ios::floatfield);

cout << "Recapitulating, here are the figures:\n";

for (year = FirstYear; year <= FirstYear + Years; year++)

{

cout << year << ": $" << income[year] << "\n";

total += income[year];

}

cout << "busywork values: " << busywork;

} // end of try block

catch (LimitArE::SonOfBad & bi) // 1st handler

{

cout << "LimitArE exception:\n";

Ngày đăng: 07/07/2014, 06:20

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN