Sun Studio 12 Update 1: C++ User's Guide

14.3.1 Output Using iostream

Output using iostream usually relies on the overloaded left-shift operator (<<) which, in the context of iostream, is called the insertion operator. To output a value to standard output, you insert the value in the predefined output stream cout. For example, given a value someValue, you send it to standard output with a statement like:


cout << someValue;

The insertion operator is overloaded for all built-in types, and the value represented by someValue is converted to its proper output representation. If, for example, someValue is a float value, the << operator converts the value to the proper sequence of digits with a decimal point. Where it inserts float values on the output stream, << is called the float inserter. In general, given a type X, << is called the X inserter. The format of output and how you can control it is discussed in the ios(3CC4) man page.

The iostream library does not support user-defined types. If you define types that you want to output in your own way, you must define an inserter (that is, overload the << operator) to handle them correctly.

The << operator can be applied repetitively. To insert two values on cout, you can use a statement like the one in the following example:


cout << someValue << anotherValue;

The output from the above example will show no space between the two values. So you may want to write the code this way:


cout << someValue << " " << anotherValue;

The << operator has the precedence of the left shift operator (its built-in meaning). As with other operators, you can always use parentheses to specify the order of action. It is often a good idea to use parentheses to avoid problems of precedence. Of the following four statements, the first two are equivalent, but the last two are not.


cout << a+b;              // + has higher precedence than <<
cout << (a+b);
cout << (a&y);            // << has precedence higher than &
cout << a&y;            // probably an error: (cout << a) & y

14.3.1.1 Defining Your Own Insertion Operator

The following example defines a string class:


#include <stdlib.h>
#include <iostream.h>


class string {
private:
    char* data;
    size_t size;

public:
    // (functions not relevant here)

    friend ostream& operator<<(ostream&, const string&);
    friend istream& operator>>(istream&, string&);
};

The insertion and extraction operators must in this case be defined as friends because the data part of the string class is private.


ostream& operator<< (ostream& ostr, const string& output)
{    return ostr << output.data;}

Here is the definition of operator<< overloaded for use with strings.


cout << string1 << string2;

operator<< takes ostream& (that is, a reference to an ostream) as its first argument and returns the same ostream, making it possible to combine insertions in one statement.

14.3.1.2 Handling Output Errors

Generally, you don’t have to check for errors when you overload operator<< because the iostream library is arranged to propagate errors.

When an error occurs, the iostream where it occurred enters an error state. Bits in the iostream’s state are set according to the general category of the error. The inserters defined in iostream ignore attempts to insert data into any stream that is in an error state, so such attempts do not change the iostream’s state.

In general, the recommended way to handle errors is to periodically check the state of the output stream in some central place. If there is an error, you should handle it in some way. This chapter assumes that you define a function error, which takes a string and aborts the program. error is not a predefined function. See 14.3.9 Handling Input Errors for an example of an error function. You can examine the state of an iostream with the operator !,which returns a nonzero value if the iostream is in an error state. For example:


if (!cout) error("output error");

There is another way to test for errors. The ios class defines operator void *(), so it returns a NULL pointer when there is an error. You can use a statement like:


if (cout << x) return; // return if successful

You can also use the function good, a member of ios:


if (cout.good()) return; // return if successful

The error bits are declared in the enum:


enum io_state {goodbit=0, eofbit=1, failbit=2,
badbit=4, hardfail=0x80};

For details on the error functions, see the iostream man pages.

14.3.1.3 Flushing

As with most I/O libraries, iostream often accumulates output and sends it on in larger and generally more efficient chunks. If you want to flush the buffer, you simply insert the special value flush. For example:


cout << "This needs to get out immediately." << flush;
 

flush is an example of a kind of object known as a manipulator, which is a value that can be inserted into an iostream to have some effect other than causing output of its value. It is really a function that takes an ostream& or istream& argument and returns its argument after performing some actions on it (see 14.7 Manipulators).

14.3.1.4 Binary Output

To obtain output in the raw binary form of a value, use the member function write as shown in the following example. This example shows the output in the raw binary form of x.


cout.write((char*)&x, sizeof(x));

The previous example violates type discipline by converting &x to char*. Doing so is normally harmless, but if the type of x is a class with pointers, virtual member functions, or one that requires nontrivial constructor actions, the value written by the above example cannot be read back in properly.