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

Chapter 24: Using Stream I/O 325

The result from a sample execution appears as follows:

=============================

Chester, 12345 56.60

name = Chester,account = 12345, balance = 56.6 Chester, 56.6 12345

=============================

Arthur, 34567 67.50

name = Arthur,account = 34567, balance = 67.5 Arthur, 67.5 34567

=============================

Trudie, 56x78 78.90 Error parsing string

=============================

Valerie, 78901 89.10

name = Valerie,account = 78901, balance = 89.1 Valerie, 89.1 78901

=============================

Press any key to continue . . .

Reflect a second before continuing. Notice how the program was able to resynch itself after the error in the input file. Notice, also, the simplicity of the heart of the program, the parseString() function. Consider what this function would look like without the benefit of the istringstream class.

Manipulating Manipulators

You can use stream I/O to output numbers and character strings by using default formats. Usually the defaults are fine, but sometimes they don’t cut it.

For example, I was less than tickled when the total from the result of a financial calculation from a recent program appeared as 249.600006 rather than 249.6 (or, better yet, 249.60). There must be a way to bend the defaults to my desires. True to form, C++ provides not one but two ways to control the format of output.

Depending on the default settings of your compiler, you may get 249.6 as your output. Nevertheless, you really want 249.60.

First, you can control the format by invoking a series of member functions on the stream object. For example, the number of significant digits to display is set by using the function precision() as follows:

326 Part V: Optional Features

#include <iostream.h>

void fn(float interest, float dollarAmount)

{

cout << “Dollar amount = “; cout.precision(2);

cout << dollarAmount; cout.precision(4); cout << interest

<< “\n”;

}

In this example, the function precision() sets the precision to 2 immedi­ ately before outputting the value dollarAmount. This gives you a number such as 249.60, the type of result you want. It then sets the precision to 4 before outputting the interest.

A second approach uses what are called manipulators. (Sounds like someone behind the scenes of the New York Stock Exchange, doesn’t it? Well, manipula­ tors are every bit as sneaky.) Manipulators are objects defined in the include file iomanip.h to have the same effect as the member function calls. (You must include iomanip.h to have access to the manipulators.) The only advantage to manipulators is that the program can insert them directly into the stream rather than resort to a separate function call.

If you rewrite the preceding example to use manipulators, the program appears as follows:

#include <iostream.h> #include <iomanip.h>

void fn(float interest, float dollarAmount)

{

cout << “Dollar amount = “

<<setprecision(2) << dollarAmount

<<setprecision(4) << interest

<<“\n”;

}

The most common manipulators and their corresponding meanings are shown in Table 24-4.

Table 24-4

Common Manipulators and Stream

 

Format Control Functions

Manipulator

Member Function

Description

dec

flags(10)

Set radix to 10

 

 

 

hex

flags(16)

Set radix to 16

 

 

 

 

 

 

Chapter 24: Using Stream I/O 327

 

 

 

 

 

 

 

 

 

Manipulator

Member Function

Description

 

 

oct

flags(8)

Set radix to 8

 

 

 

 

 

 

setfill(c)

fill(c)

Set the fill character to c

 

 

 

 

 

 

setprecision(c)

precision(c)

Set display precision to c

 

 

 

 

 

 

setw(n)

width(n)

Set width of field to n characters*

 

 

 

 

 

*This returns to its default value after the next field is output.

Watch out for the width parameter (width() function and setw() manipula­ tor). Most parameters retain their value until they are specifically reset by a subsequent call, but the width parameter does not. The width parameter is reset to its default value as soon as the next output is performed. For exam­ ple, you might expect the following to produce two eight-character integers:

#include

<iostream.h>

 

#include <iomanip.h>

 

void fn()

 

 

{

 

 

cout << setw(8)

// width is 8...

 

<< 10

//...for the 10, but...

 

<< 20

//...default for the 20

}

<< “\n”;

 

 

 

 

 

 

What you get, however, is an eight-character integer followed by a twocharacter integer. To get two eight-character output fields, the following is necessary:

#include <iostream.h> #include <iomanip.h> void fn()

{

cout <<

setw(8)

// set the width...

<<

10

 

<<

setw(8)

//...now reset it

<<20

<<“\n”;

}

Thus, if you have several objects to output and the default width is not good enough, you must include a setw() call for each object.

Which way is better, manipulators or member function calls? Member func­ tions provide a bit more control because there are more of them. In addition, the member functions always return the previous setting so you know how to restore it (if you want to). Finally, a query version of each member function exists to enable you to just ask what the current setting is without changing it, as shown in the following example:

328 Part V: Optional Features

#include <iostream.h> void fn(float value)

{

int previousPrecision;

//...doing stuff here...

//you can ask what the current precision is: previousPrecision = cout.precision();

//or you can save the old value when you change it previousPrecision = cout.precision(2);

cout << value;

//now restore the precision to previous value cout.precision(previousPrecision);

//...do more neat stuff...

}

Even with all these features, the manipulators are more common than member function calls, probably because they look neat. Use whatever you prefer, but be prepared to see both in other peoples’ code.

Chapter 25

Handling Errors — Exceptions

In This Chapter

Introducing an exceptional way of handling program errors

Finding what’s wrong with good ol’ error returns

Examining throwing and catching exceptions

Packing more heat into that throw

Iknow that it’s hard to accept, but occasionally functions don’t work prop­ erly — not even mine. The traditional means of reporting failure is to return

some indication to the caller. C++ includes a new, improved mechanism for cap­ turing and handling errors called exceptions. An exception is “a case in which

a rule or principle does not apply.” Exception is also defined as an objection to something. Either definition works: An exception is an unexpected (and presumably objectionable) condition that occurs during the execution of the program.

The exception mechanism is based on the keywords try, catch, and throw (that’s right, more variable names that you can’t use). In outline, it works like this: A function trys to get through a piece of code. If the code detects a prob­ lem, it throws an error indication that the calling function must catch.

The following code snippet demonstrates how that works in 1s and 0s:

//

//FactorialException - demonstrate exceptions using

//

a factorial function

//

 

#include <cstdio>

 

#include <cstdlib>

 

#include <iostream>

 

using namespace std;

 

// factorial - compute factorial int factorial(int n)

{

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

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

330 Part V: Optional Features

{

throw string(“Argument for factorial negative”);

}

// 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 will never get here

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

}

// control passes here catch(string error)

{

cout << “Error occurred: “ << error << endl;

}

catch(...)

{

cout << “Default catch “ << endl;

}

//wait until user is ready before terminating program

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

return 0;

}

main() starts out by creating a block outfitted with the try keyword. Within this block, it can proceed on the way it would if the block were not present. In this case, main() attempts to calculate the factorial of a negative number. Not to be hoodwinked, the clever factorial() function detects the bogus request and throws an error indication using the throw keyword. Control passes to the catch phrase, which immediately follows the closing brace of the try block. The second call to factorial() is not performed.