Rogue Wave banner
Previous fileTop of DocumentContentsIndexNext file

13.2 Copying and Assigning Stream Objects

Stream objects cannot simply be copied and assigned. Let us consider a practical example to see what this means. A program writes data to a file if a file name is specified on program call, or to the standard output stream cout if no file name is specified. You should write to one output stream in your program; this stream can be either a file stream or the standard output stream. The most obvious way to do this is to declare an output file stream object and assign it to cout, or to use cout directly. However, you can't do it this way:

This solution is bad for at least three reasons. First, the predefined streams cin, cout, cerr, and clog have special properties and are treated differently from other streams. If you could reassign them, as done with cout in the example above, you would lose their special properties. Second, assignment and copying of streams is hazardous. Even if the assignment of the output stream fil compiles, your program is likely to crash afterwards.14 Finally, the base class for iostreams has private assignment and coy constructors to prevent you from doing this.


NOTE: Stream objects must never be copied or assigned to each other.

13.2.1 Copying a Stream's Data Members

To achieve the equivalent effect of a copy, you might consider copying each data member individually. This can be done as follows:

//1The copyfmt() function copies all data from the standard output stream cout to the output file stream out, except the error state and the stream buffer. There is a function exceptions() that allows you to copy the exception mask separately, as in cout.exceptions(fil.exceptions()), but you need not do this explicitly, since copyfmt() already copies the exception mask.
//2Here the error state is copied.
//3Here the stream buffer pointer is copied.

Please note the little snag here. After the call to rdbuf(), the buffer is shared between the two streams, as shown in Figure 30:

Figure 30 -- Copying a stream's internal data results in a shared buffer

13.2.2 Sharing Stream Buffers Inadvertently

Whether or not you intend to share a stream buffer among streams depends on your application. In any case, it is important that you realize the stream buffer is shared after a call to rdbuf(); in other words, you must monitor the lifetime of the stream buffer object and make sure it exceeds the lifetime of the stream. In our little example above, we use the standard output stream's buffer. Since the standard streams are static objects, their stream buffers have longer lifetimes that most other objects, so we are safe. However, whenever you share a stream buffer among other stream objects, you must carefully consider the stream buffer's lifetime.

The example above has another disadvantage we haven't considered yet, as shown in the following code:

//1Copy the values of member variables (other than the streambuffer and the iostate) in cout to out.
//2Set state flags for out to the current state of cout.
//3Replace out's streambuffer with cout's streambuffer.

As we copy the standard output stream's entire internal data, we also copy its special behavior. For instance, the standard output stream is synchronized with the standard input stream. (See Chapter 14 for further details.) If our output file stream out is a copy of cout, it is forced to synchronize its output operations with all input operations from cin. This might not be desired, especially since synchronization is a time-consuming activity. Here is a more efficient approach using only the stream buffer of the standard output stream:

//1Instead of creating a file stream object, which already contains a file buffer object, we construct a separate file buffer object on the heap that we can hand over to an output stream object if needed. This way we can delete the file buffer object if not needed. In the original example, we constructed a file stream object with no chance of eliminating the file buffer object if not used.
//2An output stream is constructed. The stream has either the standard output stream's buffer, or a file buffer connected to a file.
//3If the program is provided with a file name, the file is opened and connected to the file buffer object. (Note that you must ensure that the lifetime of this stream buffer object exceeds the lifetime of the output stream that uses it.) The open() function returns a pointer to the file buffer object. This pointer is used to construct the output stream object.
//4If no file name is provided, the standard output stream's buffer is used.

As in the original example, out inserts through the standard output stream's buffer, but lacks the special properties of a standard stream.

Here is an alternative solution that uses file descriptors, a nonstandard feature of Rogue Wave's implementation of the standard iostreams15:

//1If the program is provided with a file name, the file is opened and connected to the file buffer object.
//2Otherwise, the output stream's file buffer is connected to the standard input stream stdout whose file descriptor is 1.

The effect is the same as in the previous solution, because the standard output stream cout is connected to the C standard input file stdout. This is the simplest of all solutions, because it doesn't involve reassigning or sharing stream buffers. The output file stream's buffer is simply connected to the right file. However, this is a nonstandard solution, and may decrease portability.

13.2.3 Using Pointers or References to Streams

If you do not want to deal with stream buffers at all, you can also use pointers or references to streams instead. Here is an example:

//1A pointer to an ostream is used. (Note that it cannot be a pointer to an ofstream, because the standard output stream cout is not a file stream, but a plain stream of type ostream.)
//2A file stream for the named output file is created on the heap and assigned to the pointer, in case a file name is provided.
//3Otherwise, a pointer to cout is used.
//4Output is written through the pointer to either cout or the named output file.

An alternative approach could use a reference instead of a pointer:

Working with pointers and references has a drawback: you must create an output file stream object on the heap and, in principle, you must worry about deleting the object again, which might lead you into other dire straits.

In summary, creating a copy of a stream is not trivial and should only be done if you really need a copy of a stream object. In many cases, it is more appropriate to use references or pointers to stream objects instead, or to share a stream buffer between two streams.


NOTE: Never create a copy of a stream object when a reference or a pointer to the stream object would suffice, or when a shared stream buffer would solve the problem.

Previous fileTop of DocumentContentsIndexNext file

OEM Edition, ©Copyright 1999, Rogue Wave Software, Inc.
Contact Rogue Wave about documentation or support issues.