14.2.2. Overloading the Input Operator >>

Image

Ordinarily the first parameter of an input operator is a reference to the stream from which it is to read, and the second parameter is a reference to the (nonconst) object into which to read. The operator usually returns a reference to its given stream. The second parameter must be nonconst because the purpose of an input operator is to read data into this object.

The Sales_data Input Operator

As an example, we’ll write the Sales_data input operator:

istream &operator>>(istream &is, Sales_data &item)
{
    double price;  // no need to initialize; we'll read into price before we use it
    is >> item.bookNo >> item.units_sold >> price;
    if (is)        // check that the inputs succeeded
        item.revenue = item.units_sold * price;
    else
        item = Sales_data(); // input failed: give the object the default state
    return is;
}

Except for the if statement, this definition is similar to our earlier read function (§ 7.1.3, p. 261). The if checks whether the reads were successful. If an IO error occurs, the operator resets its given object to the empty Sales_data. That way, the object is guaranteed to be in a consistent state.


Image Note

Input operators must deal with the possibility that the input might fail; output operators generally don’t bother.


Errors during Input

The kinds of errors that might happen in an input operator include the following:

• A read operation might fail because the stream contains data of an incorrect type. For example, after reading bookNo, the input operator assumes that the next two items will be numeric data. If nonnumeric data is input, that read and any subsequent use of the stream will fail.

• Any of the reads could hit end-of-file or some other error on the input stream.

Rather than checking each read, we check once after reading all the data and before using those data:

if (is)        // check that the inputs succeeded
    item.revenue = item.units_sold * price;
else
    item = Sales_data(); // input failed: give the object the default state

If any of the read operations fails, price will have an undefined value. Therefore, before using price, we check that the input stream is still valid. If it is, we do the calculation and store the result in revenue. If there was an error, we do not worry about which input failed. Instead, we reset the entire object to the empty Sales_data by assigning a new, default-initialized Sales_data object to item. After this assignment, item will have an empty string for its bookNo member, and its revenue and units_sold members will be zero.

Putting the object into a valid state is especially important if the object might have been partially changed before the error occurred. For example, in this input operator, we might encounter an error after successfully reading a new bookNo. An error after reading bookNo would mean that the units_sold and revenue members of the old object were unchanged. The effect would be to associate a different bookNo with those data.

By leaving the object in a valid state, we (somewhat) protect a user that ignores the possibility of an input error. The object will be in a usable state—its members are all defined. Similarly, the object won’t generate misleading results—its data are internally consistent.


Image Best Practices

Input operators should decide what, if anything, to do about error recovery.


Indicating Errors

Some input operators need to do additional data verification. For example, our input operator might check that the bookNo we read is in an appropriate format. In such cases, the input operator might need to set the stream’s condition state to indicate failure (§ 8.1.2, p. 312), even though technically speaking the actual IO was successful. Usually an input operator should set only the failbit. Setting eofbit would imply that the file was exhausted, and setting badbit would indicate that the stream was corrupted. These errors are best left to the IO library itself to indicate.


Exercises Section 14.2.2

Exercise 14.9: Define an input operator for your Sales_data class.

Exercise 14.10: Describe the behavior of the Sales_data input operator if given the following input:

(a) 0-201-99999-9 10 24.95

(b) 10 24.95 0-210-99999-9

Exercise 14.11: What, if anything, is wrong with the following Sales_data input operator? What would happen if we gave this operator the data in the previous exercise?

istream& operator>>(istream& in, Sales_data& s)
{
    double price;
    in >> s.bookNo >> s.units_sold >> price;
    s.revenue = s.units_sold * price;
    return in;
}

Exercise 14.12: Define an input operator for the class you used in exercise 7.40 from § 7.5.1 (p. 291). Be sure the operator handles input errors.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset