Understanding the Utility of Iostreams in C++

The iostream classes are the first library classes we encounter when we begin with C++. The primary services that we deal with these classes is solving general I/O problems. After all, this is what the name apparently means. But the beauty is that it represents standard I/O, files, and even blocks of memory and they look the same with one interface. The article explores some of the facets of this incredible class with simple example.

What is iostream in C++?

The C++ language deals with I/O a bit differently that other languages. It is handled by a family of types prescribed in the standard library. The I/O to and from device files, consoles, and in-memory I/O, such as from string, are all handled by these types. The primary functions defined are read/write operations of the built-in types. Therefore, the I/O stream library provides formatted as well as unformatted buffered I/O of text as well as numeric values.

What is a Stream in C++?

When a sequence of bytes flows from one interface to another, it can be called a stream. This sequence of bytes represents information that may be further processed or used as is, depending upon the context. When the flow of bytes sequence is main memory inbound, such as from devices like a keyboard, disk, or network connection, it is called an input operation. And, when the flow of bytes is main memory outbound to a device such as printer, disk, network connection, or display screen, it is called an output operation. The sequence of bytes can have different meaning depending upon the context. It can represent characters, graphical images, raw data, audio/video information, or any other type depending upon the context of use.

The I/O mechanism must be able to transfer the sequence of bytes in a consistent and efficient manner. But, the problem is that the devices associated with the I/O are often are slow in operation. For example, the mechanical rotation of a disk, input coming from a keyboard strokes, or sequence of bytes from remote interface require considerably more time than the time required by the processor to process the data. Therefore, to mitigate the time gap so that it does not lag the overall performance of the I/O operation, the C++ stream library classes are fine tuned for optimal performance.

Another problem is that data usually comes in different formats. Some come as an unformatted I/O format, which require low-level I/O processing, and some comes in formatted I/O format, which require high-level I/O capabilities. The unformatted I/O specified by sequence of bytes requires processing of individual bytes during its transfer from device to memory or memory to device. Such data often comes in high volume and require fast processing capabilities. In formatted I/O, the sequence of bytes forms a unit of meaningful combination of bytes such as characters, floating-point numbers, integers, strings, or any other user-defined types. Handling I/O with such data units is convenient but inefficient, especially when they come in high volume. The standard C++ stream library supports both the format and does both high- and low-level I/O processing, respectively.

Character Sets

Previously, the C++ library used to support only input and output of chars which carried only one byte of information, such as those we find with the ASCII (American Standard Code for information Interchange) (8-bit/1-byte) character set. But later, it introduced Unicode characters, which represent extensive international character sets, the I/O capabilities of C++ encompassed them within its framework. For example, the type wchar_t (4-byte/32-bit) is a later inclusion and can store Unicode characters. Also, C++11 introduced other types, such as char16_t and char32_t, to represent character types that require explicit sizes. The stream classes were redesigned as specialized templates for dealing with char and wchar_t, respectively.

Stream Classes and Objects

A C++ program includes a <iostream> header to avail the basic service required for all stream I/O operations. There are four objects defined by iostream: cin, cout, cerr, and clog that correspond to the standard input stream, standard output stream, standard error stream (unbuffered), and standard error stream (buffered), respectively. Each of them provides both formatted and unformatted I/O services.

There is another header, called <iomanip>, which provides parameterized stream manipulators for performing formatted I/O such as setw, setbase, setprecision, setfill, setiosflags, and more.

The <fstream> header is particularly used in association with file processing and provides services for file I/O.

The library stream classes form hierarchy as follows. Figure 1 gives you a quick idea on some of their traits.

The library stream classes' hierarchy
Figure 1: The library stream classes’ hierarchy

  • The ios_base class is the base class for all stream classes of the standard I/O library. It defines common properties of all stream objects, independent of their character type, such as functions for state and format flags.
  • The basic_ios is a template class derived from ios_base. It defines a stream component independent of input or output, but depends on character types and their traits. They provide the buffer definition, such as the object from the template class basic_streambuf. They are used for the read/write scheme.
  • The basic_istream and basic_ostream are template classes derived from basic_ios. They define the objects used for read and write operations, respectively. These classes too are parameterized with character types and its traits.
  • The basic_streambuf is the core of the I/O stream library. They provide the interface for read and write operations of the stream.

We accept input via descendants of the istream classes and output via descendants of the ostream classes. The iostream classes combine both and provide an object to do input as well as output. Apart from these, the iostream library provides classes for ifstream file input, ofstream for file output, and fstream for both input as well as output operation on files. Similarly, there are classes, such as istringstream, ostringstream, and stringstream, for I/O operation with string classes. They almost have a same interface whether we work with a file, standard I/O, memory, or string object. The stream classes discussed above are the templatized versions to leverage generic type I/O operation.

A Quick Example

Here is a quick demonstration of object input/output by overloading ostream and istream operators. The properties of the rudimentary Date class object get inserted and extracted conveniently using stream operators.

#ifndef DATE_H
#define DATE_H

#include <string>

class Date
{
   int day, month, year;
public:
   Date();
   friend std::ostream& operator<<(std::ostream&,
      const Date&);
   friend std::istream& operator>>(std::istream&, Date&);

};

#endif   // DATE_H

#include "date.h"
#include <ctime>
#include <sstream>
#include <iomanip>

using namespace std;

Date::Date()
{
   time_t today = time(0);
   tm *lt = localtime(&today);
   year = lt->tm_year + 1900;
   month = lt->tm_mon + 1;
   day = lt->tm_mday;
}

ostream& operator<<(ostream& os, const Date& d) {
   os << d.day << '/'<<d.month<<'/'<<d.year;
   return os;
}

istream& operator>>(istream& is, Date& d) {
   is >> d.day >> d.month >> d.year;
   return is;
	}

#include <iostream>
#include "date.h"
using namespace std;

int main()
{
   Date d;
   Date d2;

   cout<<d<<endl;
   cout<<"Enter Date (in dd/mm/yyyy):";
   cin>>d2;
   cout<<d2;

   return 0;
}

Conclusion

The istream object provides formatted and unformatted input capabilities. There are member functions, like get and getline, that overcome the limitation of extraction operators (>>), which skip white-space characters such as blanks, tabs, and newline. Similarly, ostream provides formatted and unformatted output capabilities. There are member functions, such as put, along with the stream insertion operator (<<) to output standard data types. We, however, can overload these operators to give a new meaning as we have done in the example above. Note that here we have merely scratched the surface of iostream library. There are many other aspects. Stay tuned for more.

Manoj Debnath
Manoj Debnath
A teacher(Professor), actively involved in publishing, research and programming for almost two decades. Authored several articles for reputed sites like CodeGuru, Developer, DevX, Database Journal etc. Some of his research interest lies in the area of programming languages, database, compiler, web/enterprise development etc.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read