I'm trying to learn a bit more about how I/O streams work in C++, and I'm really confused at when to use what.
What exactly is a
Stream buffers represent input or output devices and provide a low level interface for unformatted I/O to that device. Streams, on the other hand, provide a higher level wrapper around the buffer by way of basic unformatted I/O functions and especially via formatted I/O functions (i.e.,
operator>> overloads). Stream objects may also manage a stream buffer's lifetime.
For example a file stream has an internal file stream buffer. The stream manages the lifetime of the buffer and the buffer is what provides actual read and write capabilities to a file. The stream's formatting operators ultimately access the stream buffer's unformatted I/O functions, so you only ever have to use the stream's I/O functions, and don't need to touch the buffer's I/O functions directly.
Another way to understand the differences is to look at the different uses they make of locale objects. Streams use the facets that have to do with formatting such as
num_get. You can also expect that the overloads of stream
operator>> for custom time or money data types will use the time and money formatting facets. Stream buffers, however, use the codecvt facets in order to convert between the units their interface uses and bytes. So, for example, the interface for
char16_t and so
basic_streambuf<char16_t> internally uses
codecvt<char16_t, char, mbstate_t> by default to convert the formatted
char16_t units written to the buffer to
char units written to the underlying device. So you can see that streams are mostly for formatting and stream buffers provide a low level interface for unformatted input or output to devices which may use a different, external encoding.
You can use a stream buffer when you want only unformatted access to an I/O device. You can also use stream buffers if you want to set up multiple streams that share a stream buffer (though you'll have to carefully manage the lifetime of the buffer). There are also special purpose stream buffers you might want to use, such as
wbuffer_convert in C++11 which acts as a façade for a
basic_streambuf<char> to make it look like a wide character stream buffer. It uses the codecvt facet it's constructed with instead of using the codecvt facet attached to any locale. You can usually achieve the same effect by simply using a wide stream buffer imbued with a locale that has the appropriate facet.