1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
Extending the standard stream (ti(FILE)) approach, well known from the
bf(C) programming language, bf(C++) offers an em(input/output) (i(I/O))
library hi(I/O library) based on tt(class) concepts.
All bf(C++) I/O facilities are defined in the namespace ti(std). The tt(std::)
prefix is omitted below, except for situations where this would result in
ambiguities.
Earlier (in chapter ref(FirstImpression)) we've seen several examples of the
use of the bf(C++) I/O library, in particular showing insertion
operator (lshift()) and the extraction operator (rshift()).
In this chapter we'll cover I/O in more detail.
The discussion of input and output facilities provided by the bf(C++)
programming language heavily uses the tt(class) concept and the notion of
member functions. Although class construction has not yet been covered (for
that see chapter ref(Classes)) and although em(inheritance) is not covered
formally before chapter ref(INHERITANCE), it is quite possible to discuss I/O
facilities long before the technical background of class construction has been
covered.
Most bf(C++) I/O classes have names starting with ti(basic_) (like
tt(basic_ios)). However, these ti(basic_) names are not regularly found in
bf(C++) programs, as most classes are also defined through ti(using)
declarations like:
verb( using ios = basic_ios<char>;)
Since bf(C++) supports various kinds of character types (e.g., ti(char),
ti(wchar_t)), I/O facilities were developed using the emi(template) mechanism
allowing for easy conversions to character types other than the traditional
tt(char) type. As elaborated in chapter ref(TEMPLATES), this also allows the
construction of i(generic software), that could thereupon be used for any
particular type representing characters. So, analogously to the above
using declaration there exists a
verb( using wios = basic_ios<wchar_t>;)
This way, tt(wios) can be used for the tt(wchar_t) type. Because of the
existence of these type definitions, the tt(basic_) prefix was omitted from
the annotations() without loss of continuity. The annotations() primarily
focus on the standard 8-bits tt(char) type.
Iostream objects can+em(not) be declared using standard
i(forward declaration)s, like:
verb( class std::ostream; // now erroneous)
Instead, to i(declare iostream classes) the tthi(iosfwd) header file
should be included:
verb( #include <iosfwd> // correct way to declare iostream classes)
Using bf(C++) I/O offers the additional advantage of
emi(type safety). Objects (or plain values) are inserted into
streams. Compare this to the situation commonly encountered in bf(C) where the
ti(fprintf) function is used to indicate by a format string what kind of
value to expect where. Compared to this latter situation bf(C++)'s
em(iostream) approach immediately uses the objects where their values should
appear, as in
verb( cout << "There were " << nMaidens << " virgins present\n";)
The compiler notices the type of the tt(nMaidens) variable, inserting
its proper value at the appropriate place in the sentence inserted into
the tt(cout) iostream.
Compare this to the situation encountered in bf(C). Although bf(C) compilers
are getting smarter and smarter, and although a well-designed
bf(C) compiler may warn you for a mismatch between a format specifier and the
type of a variable encountered in the corresponding position of the argument
list of a tt(printf) statement, it can't do much more than em(warn) you.
The em(type safety) seen in bf(C++) em(prevents) you from making type
mismatches, as there are no types to match.
Apart from this, em(iostreams) offer more or less the same set of
possibilities as the standard tt(FILE)-based I/O used in bf(C): files can be
opened, closed, positioned, read, written, etc.. In bf(C++) the basic tt(FILE)
structure, as used in bf(C), is still available. But bf(C++) adds to this I/O
based on classes, resulting in type safety, extensibility, and a clean design.
The i(ANSI/ISO) standard specifies architecture independent I/O. Not all of
the standard's specifications are covered in this chapter, as they often rely
on inheritance and polymorphism, which topics are formally covered by chapters
ref(INHERITANCE) and ref(POLYMORPHISM). Some examples are
offered in chapter ref(CONCRETE), and in this chapter references to
specific sections in other chapters are given where appropriate.
figure(iostreams/ioclasses)(Central I/O Classes)(IOCLASSESFIG)
This chapter is organized as follows (see also fig(IOCLASSESFIG)):
itemization(
it() The class ti(ios_base) is the foundation upon which the
iostream I/O library was built. It defines the core of all I/O operations and
offers, among other things, facilities for inspecting the
i(state of I/O streams) and facilities for i(output formatting).
it() The class ti(ios) is directly em(derived) from
tt(ios_base). Every class of the I/O library doing input or output is itself
derived from this tt(ios) class, and therefore em(inherits) its (and, by
implication: tt(ios_base)'s) capabilities. The reader is urged to keep this in
mind while reading this chapter. The concept of inheritance is not discussed
here, but rather in chapter ref(INHERITANCE).nl()
The class tt(ios) is important because it implements communication with a
em(buffer) which is used by streams. This buffer is a ti(streambuf) object
which is responsible for the actual I/O to/from the actually used em(device),
which might be a file, a keyboard, a screen, an Internet connection, etc.,
etc.. Consequently tt(iostream) objects do not perform I/O operations
themselves, but leave these operations to the (stream)buffer objects with
which they are associated.
it() Next, basic bf(C++) output facilities are discussed. The basic
class used for output operations is ti(ostream), defining the
i(insertion operator) as well as other facilities writing information to
streams. Apart from inserting information into files it is possible to insert
information into i(memory buffers), for which the ti(ostringstream) class is
available. Formatting output is to a great extent possible using the
facilities defined in the tt(ios) class, but it is also possible to
em(insert) emi(formatting commands) directly into streams using
hi(manipulator)em(manipulators). This aspect of bf(C++) output is
discussed as well.
it() Basic bf(C++) input facilities are implemented by the ti(istream)
class. This class defines the i(extraction operator) and related input
facilities. Comparably to inserting information into memory buffers (using
tt(ostringstream)) a class ti(istringstream) is available to extract
information from memory buffers.
it() The class ti(iostream) combines the facilities offered by
tt(istream) and tt(ostream). Thus, tt(iostream) objects can be used to read
em(and) write from the same object.
it() The class tt(fstream) is a frequently encountered example of an
tt(iostream) class: tt(fstream) objects are used to read and write from the
same file, which is often used in programs processing data bases. In this
chapter topics like i(reading and writing) from the same stream and
i(mixing bf(C) and bf(C++) I/O) using ti(filebuf) objects are also
covered. Other I/O related topics are covered elsewhere in the annotations()
(cf. section ref(OSYNC) and chapter ref(CONCRETE)).
)
Stream objects have a limited but important role: they are the interface
between, on the one hand, the objects to be input or output and, on the other
hand, the tt(streambuf), which is responsible for the actual input and output
to the i(device) accessed by a tt(streambuf) object.
This approach allows us to construct a new kind of tt(streambuf) for a new
kind of device, and use that streambuf in combination with the `good old'
tt(istream)- and tt(ostream)-class facilities. It is important to understand
the distinction between the formatting roles of iostream objects and the
buffering interface to an external device as implemented in a tt(streambuf)
object. Interfacing to new devices (like hi(socket)em(sockets) or
hi(file descriptor)em(file descriptors)) requires the construction of a new
kind of tt(streambuf), rather than a new kind of tt(istream) or tt(ostream)
object. A emi(wrapper class) may be constructed around the tt(istream) or
tt(ostream) classes, though, to ease the access to a special device. This is
how the stringstream classes were constructed.
|