
lafore_robert_objectoriented_programming_in_c
.pdf

576 |
Chapter 12 |
|
TABLE 12.7 Continued
|
Function |
Purpose |
|
seekp(position) |
Set distance in bytes of file pointer from start of file. |
|
seekp(position, seek_dir) |
Set distance in bytes of file pointer, from specified place in |
|
|
file. seek_dir can be ios::beg, ios::cur, or ios::end. |
|
pos = tellp() |
Return position of file pointer, in bytes. |
|
|
|
The iostream and the _withassign Classes
The iostream class, which is derived from both istream and ostream, acts only as a base class from which other classes, specifically iostream_withassign, can be derived. It has no functions of its own (except constructors and destructors). Classes derived from iostream can perform both input and output.
There are three _withassign classes:
•istream_withassign, derived from istream
•ostream_withassign, derived from ostream
•iostream_withassign, derived from iostream
These _withassign classes are much like those they’re derived from except that they include overloaded assignment operators so their objects can be copied.
Why do we need separate copyable and uncopyable stream classes? In general, it’s not a good idea to copy stream class objects. The reason is that each such object is associated with a particular streambuf object, which includes an area in memory to hold the object’s actual data. If you copy the stream object, it causes confusion if you also copy the streambuf object.
However, in a few cases it’s important to be able to copy a stream.
Accordingly, the istream, ostream, and iostream classes are made uncopyable (by making their overloaded copy constructors and assignment operators private), while the _withassign classes derived from them can be copied.
Predefined Stream Objects
We’ve already made extensive use of two predefined stream objects that are derived from the _withassign classes: cin and cout. These are normally connected to the keyboard and display, respectively. The two other predefined objects are cerr and clog.
•cin, an object of istream_withassign, normally used for keyboard input
•cout, an object of ostream_withassign, normally used for screen display


578 |
Chapter 12 |
|
TABLE 12.9 Functions for Error Flags
Function |
Purpose |
||
int |
= |
eof(); |
Returns true if EOF flag set |
int |
= |
fail(); |
Returns true if failbit or badbit or hardfail flag set |
int |
= |
bad(); |
Returns true if badbit or hardfail flag set |
int |
= |
good(); |
Returns true if everything OK; no flags set |
clear(int=0); With no argument, clears all error bits; otherwise sets specified flags, as in
clear(ios::failbit)
FIGURE 12.3
Stream status flags.
Inputting Numbers
Let’s see how to handle errors when inputting numbers. This approach applies to numbers read either from the keyboard or from disk, as we’ll see later. The idea is to check the value of goodbit, signal an error if it’s not true, and give the user another chance to enter the correct input.
while(true) |
// cycle until input OK |
{ |
|
cout << “\nEnter an integer: “; |
|
cin >> i; |
|
if( cin.good() ) |
// if no errors |
{ |
|
cin.ignore(10, ‘\n’); |
// remove newline |
break; |
// exit loop |
} |
|
cin.clear(); |
// clear the error bits |
cout << “Incorrect input”; |
|


Chapter 12
580
Thus it’s important to be able to tell the input stream not to ignore whitespace. This is handled by clearing the skipws flag:
cout << “\nEnter an integer: “; |
|
cin.unsetf(ios::skipws); |
// don’t ignore whitespace |
cin >> i; |
|
if( cin.good() ) |
|
{ |
|
// no error |
|
} |
|
// error |
|
Now if the user types Enter without any digits, the failbit will be set and an error generated. The program can then tell the user what to do, or reposition the cursor so the screen does not scroll.
Inputting Strings and Characters
The user can’t really make any serious errors inputting strings and characters, since all input, even numbers, can be interpreted as a string. However, if coming from a disk file, characters and strings should still be checked for errors, in case an EOF or something worse is encountered. Unlike the situation with numbers, you often do want to ignore whitespace when inputting strings and characters.
Error-Free Distances
Let’s look at a program in which user input to the English Distance class is checked for errors. This program simply accepts Distance values in feet and inches from the user and displays them. However, if the user commits an entry error, the program rejects the input with an appropriate explanation to the user, and prompts for new input.
The program is very simple except that the member function getdist() has been expanded to handle errors. Parts of this new code follow the approach of the fragment shown above. However, we’ve also added some statements to ensure that the user does not enter a floatingpoint number for feet. This is important because, while the feet value is an integer, the inches value is floating-point, and the user could easily become confused.
Ordinarily, if it’s expecting an integer, the extraction operator simply terminates when it sees a decimal point, without signaling an error. We want to know about such an error, so we read the feet value as a string instead of an int. We then examine the string with a homemade function isFeet(), which returns true if the string proves to be a correct value for feet. To pass the feet test, it must contain only digits, and they must evaluate to a number between –999 and 999.
(We assume that the Distance class will never be used for measuring larger feet values.) If the string passes the feet test, we convert it to an actual int with the library function atoi().


582 |
Chapter 12 |
|
while(true) |
//cycle until inches are right |
{ |
|
cout << “Enter inches: “; |
|
cin.unsetf(ios::skipws); |
//do not skip white space |
cin >> inches; |
//get inches (type float) |
if(inches>=12.0 || inches<0.0) |
|
{ |
|
cout << “Inches must be between 0.0 and 11.99\n”; |
|
cin.clear(ios::failbit); |
//”artificially” set fail bit |
} |
|
if( cin.good() ) |
//check for cin failure |
{ |
//(most commonly a non-digit) |
cin.ignore(10, ‘\n’); |
//eat the newline |
break; |
//input is OK, exit ‘while’ |
} |
|
cin.clear(); |
//error; clear the error state |
cin.ignore(10, ‘\n’); |
//eat chars, including newline |
cout << “Incorrect inches input\n”; //start again |
|
} //end while inches |
|
} |
|
//-------------------------------------------------------------- |
|
int isFeet(string str) |
//return true if the string |
{ |
// is a correct feet value |
int slen = str.size(); |
//get length |
if(slen==0 || slen > 5) |
//if no input, or too long |
return 0; |
//not an int |
for(int j=0; j<slen; j++) |
//check each character |
|
//if not digit or minus |
if( (str[j] < ‘0’ || str[j] |
> ‘9’) && str[j] != ‘-’ ) |
return 0; |
//string is not correct feet |
double n = atof( str.c_str() ); |
//convert to double |
if( n<-999.0 || n>999.0 ) |
//is it out of range? |
return 0; |
//if so, not correct feet |
return 1; |
//it is correct feet |
}
////////////////////////////////////////////////////////////////
int main() |
|
{ |
|
Distance d; |
//make a Distance object |
char ans; |
|
do |
|
{ |
|
d.getdist(); |
//get its value from user |
cout << “\nDistance = “; |
|
d.showdist(); |
//display it |
cout << “\nDo another (y/n)? “; cin >> ans;


Chapter 12
584
Writing Data
The following program writes a character, an integer, a type double, and two string objects to a disk file. There is no output to the screen. Here’s the listing for FORMATO:
//formato.cpp
//writes formatted output to a file, using <<
#include <fstream> |
//for file I/O |
||
#include <iostream> |
|
||
#include <string> |
|
||
using namespace std; |
|
||
int main() |
|
|
|
{ |
|
|
|
char ch |
= ‘x’; |
|
|
int j = |
77; |
|
|
double d = |
6.02; |
|
|
string str1 = “Kafka”; |
//strings without |
||
string str2 = “Proust”; |
// embedded spaces |
||
ofstream outfile(“fdata.txt”); |
//create ofstream object |
||
outfile |
<< |
ch |
//insert (write) data |
|
<< |
j |
|
|
<< |
‘ ‘ |
//needs space between numbers |
|
<< |
d |
|
|
<< |
str1 |
|
|
<< |
‘ ‘ |
//needs spaces between strings |
|
<< |
str2; |
|
cout << |
“File written\n”; |
|
|
return 0; |
|
|
|
} |
|
|
|
Here we define an object called outfile to be a member of the ofstream class. At the same time, we initialize it to the file FDATA.TXT. This initialization sets aside various resources for the file, and accesses or opens the file of that name on the disk. If the file doesn’t exist, it is created. If it does exist, it is truncated and the new data replaces the old. The outfile object acts much as cout did in previous programs, so we can use the insertion operator (<<) to output variables of any basic type to the file. This works because the insertion operator is appropriately overloaded in ostream, from which ofstream is derived.
When the program terminates, the outfile object goes out of scope. This calls its destructor, which closes the file, so we don’t need to close the file explicitly.
There are several potential formatting glitches. First, you must separate numbers (such as 77 and 6.02) with nonnumeric characters. Since numbers are stored as a sequence of characters,