Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ For Dummies (2004) [eng].pdf
Скачиваний:
84
Добавлен:
16.08.2013
Размер:
8.09 Mб
Скачать

Chapter 25: Handling Errors — Exceptions 335

match the int, so it’s skipped. The next catch phrase matches the int exactly, so control stops there. The final catch phrase, which would catch any object thrown, is skipped because a matching catch phrase was already found.

What Kinds of Things Can I Throw?

The thing following the throw keyword is actually an expression that creates an object of some kind. In the examples so far, I’ve thrown an int and a string object, but throw can handle any type of object. This means that you can throw almost as much information as you want. Consider the following update to the factorial program, CustomExceptionClass:

//

//CustomExceptionClass - demonstrate exceptions using

//

a factorial function

//

 

#include <cstdio>

 

#include <cstdlib>

 

#include <iostream>

 

#include <sstream>

 

using namespace std;

 

// Exception - generic exception handling class class Exception

{

public:

Exception(char* pMsg, int n, char* pFile, int nLine)

: msg(pMsg), errorValue(n), file(pFile), lineNum(nLine) {}

virtual string display()

{

ostringstream out;

out << “Error <” << msg

<<“ - value is “ << errorValue

<<“>\n”;

out << “ @” << file << “-” << lineNum << endl; return out.str();

}

protected:

// error message string msg;

int errorValue;

// file name and line number where error occurred string file;

int lineNum;

};

// factorial - compute factorial

336 Part V: Optional Features

int factorial(int n)

{

//you can’t handle negative values of n;

//better check for that condition first if (n < 0)

{

throw Exception(“Argument for factorial negative”, n, __FILE__, __LINE__);

}

// go ahead and calculate factorial int accum = 1;

while(n > 0)

{

accum *= n; n--;

}

return accum;

}

int main(int nNumberofArgs, char* pszArgs[])

{

try

{

// this will work

cout << “Factorial of 3 is “ << factorial(3) << endl;

// this will generate an exception

cout << “Factorial of -1 is “ << factorial(-1) << endl;

}

//control passes here catch(Exception e)

{

cout << “Error occurred: \n” << e.display() << endl;

}

//wait until user is ready before terminating program

//to allow the user to see the program results system(“PAUSE”);

return 0;

}

This program appears much the same as the factorial program at the beginning of this chapter. The difference is the use of a user-defined Exception class that contains more information concerning the nature of the error than a simple string contains. The factorial program is able to throw the error message, the illegal value, and the exact location where the error occurred.

__FILE__ and __LINE__ are intrinsic #defines that are set to the name of the source file and the current line number in that file, respectively.

Chapter 25: Handling Errors — Exceptions 337

The catch snags the Exception object and then uses the built-in display() member function to display the error message. The output from this program appears as follows:

Factorial of 3 is 6

Error occurred:

Error <Argument for factorial negative - value is -1>

@//cpp_programs/Chap25/CustomExceptionClass.cpp-46

Press any key to continue . . .

The Exception class represents a generic error-reporting class. However, you can inherit from this class to provide further detail for a particular type of error. For example, I can define an InvalidArgumentException class that stores the value of the invalid argument in addition to the message and loca­ tion of the error:

class InvalidArgumentException : public Exception

{

public: InvalidArgumentException(int arg,

char* pFile, int nLine)

: Exception(“Invalid argument”, pFile, nLine)

{

invArg = arg;

}

virtual void display(ostream& out)

{

Exception::display(out);

out << “Argument was “ << invArg << endl;

}

protected: int invArg;

};

The calling function automatically handles the new InvalidArgument Exception because an InvalidArgumentException is an Exception and the display() member function is polymorphic.

338 Part V: Optional Features

Chapter 26

Inheriting Multiple Inheritance

In This Chapter

Introducing multiple inheritance

Avoiding ambiguities with multiple inheritance

Avoiding ambiguities with virtual inheritance

Figuring out the ordering rules for multiple constructors

Getting a handle on problems with multiple inheritance

In the class hierarchies discussed in other chapters, each class has inher­ ited from a single parent. Such single inheritance is sufficient to describe

most real-world relationships. Some classes, however, represent the blending of two classes into one. (Sounds sort of romantic, doesn’t it.)

An example of such a class is the sleeper sofa. As the name implies, it is a sofa and a bed (although not a very comfortable bed). Thus, the sleeper sofa should be allowed to inherit bed-like properties. To address this situation, C++ allows a derived class to inherit from more than one base class. This is called multiple inheritance.

Describing the Multiple

Inheritance Mechanism

To see how multiple inheritance works, look at the sleeper sofa example. Figure 26-1 shows the inheritance graph for class SleeperSofa. Notice how this class inherits from class Sofa and from class Bed. In this way, it inherits the properties of both.

340 Part V: Optional Features

 

Bed

 

Sofa

sleep( )

weight

watchTV( )

weight

Figure 26-1:

 

SleeperSofa

 

Class

 

 

 

 

 

hierarchy of

foldOut( )

 

 

a sleeper

 

 

 

sofa.

 

 

 

The code to implement class SleeperSofa looks like the following:

//

//MultipleInheritance - a single class can inherit from

//

more than one base class

//

 

#include <cstdio>

 

#include <cstdlib>

 

#include <iostream>

 

using namespace std;

 

class Bed

{

public:

Bed(){}

void sleep(){ cout << “Sleep” << endl; } int weight;

};

class Sofa

{

public:

Sofa(){}

void watchTV(){ cout << “Watch TV” << endl; } int weight;

};

// SleeperSofa - is both a Bed and a Sofa class SleeperSofa : public Bed, public Sofa

{

public:

SleeperSofa(){}

void foldOut(){ cout << “Fold out” << endl; }

};

Chapter 26: Inheriting Multiple Inheritance 341

int main(int nNumberofArgs, char* pszArgs[])

{

SleeperSofa ss;

// you can watch TV on a sleeper sofa like a sofa...

ss.watchTV();

// Sofa::watchTV()

//...and then you can fold it out...

ss.foldOut();

// SleeperSofa::foldOut()

//...and sleep on it ss.sleep();

//wait until user is ready before terminating program

//to allow the user to see the program results system(“PAUSE”);

return 0;

}

Here the class SleeperSofa inherits from both Bed and Sofa. This is apparent from the appearance of both classes in the class declaration. SleeperSofa inherits all the members of both base classes. Thus, both of the calls ss.sleep() and ss.watchTV() are legal. You can use a SleeperSofa as a Bed or a Sofa. Plus the class SleeperSofa can have members of its own, such as foldOut(). The output of this program appears as follows:

Watch TV

Fold out

Sleep

Press any key to continue . . .

Is this a great country or what?

Straightening Out Inheritance

Ambiguities

Although multiple inheritance is a powerful feature, it introduces several possi­ ble problems. One is apparent in the preceding example. Notice that both Bed and Sofa contain a member weight. This is logical because both have a mea­ surable weight. The question is, “Which weight does SleeperSofa inherit?”

The answer is “both.” SleeperSofa inherits a member Bed::weight and a separate member Sofa::weight. Because they have the same name, unqual­ ified references to weight are now ambiguous. This is demonstrated in the following snippet: