CS320 Lecture: Input - Output: iostreams, Persistent Objects 10/2/96
revised 10/1/99
I. The Basic Mechanisms
- --- ----- ----------
A. Implementations of the C programming language come with a standard
library which includes a "standard io" package (#include <sdtio.h>)
that provides textual input-output for standard data types.
Because C++ is derived from C, programs written in C++ can use these
routines as well.
B. However, the preferred mechanism for textual IO in C++ is to use the more
object oriented approach provided the the iostreams library which is
part of all standard C++ implementations.
1. #include <iostream.h>
declares: classes istream, ostream (plus common base class ios)
variables cin, cout
2. The following input operations are provided for input streams -
e.g. cin:
a. >> for all standard data types (including library class string)
Ex: int i;
cin >> i;
N.B. Whitespace is skipped and discarded - thus cin >> c will never
set c to a space, tab, newline
b. get(), get(char) for single chars:
Ex: char c
c = cin.get();
or
cin.get(c);
N.B. For all variants of get, whitespace is NOT skipped
c. get(char *, count, delimiter) for C-style strings (array of char
with null terminator).
Ex: char c[100];
cin.get(c, 100, '\n');
or
cin.get(c, 100); // '\n' is default
Does NOT extract the delimiter.
Does store a null terminator
d. getline(char *, count, delimiter) for C-style strings
Similar to get, but does extract delimiter (but does not store
it)
e. getline(stream, string, delimiter) for library class string
i. Unfortunately, because the library string class was standardized
after the iostreams library was developed, this is NOT a method
of class istream, but rather a function that takes an istream as
its first parameter.
ii. Ex:
string s;
getline(cin, s, '\n');
or
getline(cin, s); // '\n' is the default
iii. NOT: cin.getline(s); // ERROR
f. Methods that allow looking ahead one or more characters
peek() // Returns character about to be read
putback(char) // Puts back a character that was read
unget() // Puts back last character that was read
3. The following output operations are provided for output streams -
e.g. cout:
a. << for standard data types
Ex: cout << "The answer is " << 42 << " not " << 3.14159;
b. put(char)
c. flush manipulator - forces output to the terminal immediately
(otherwise it may be buffered)
Ex: cout << "This program is about to crash" << flush;
Note: If text is output to cout and then an attempt is made to
read from cin, most implementations do an automatic flush
on cout first, assuming that the output was a prompt to
the user.
d. endl manipulator - outputs a newline character and then does a
flush.
C. Additional facilities:
1. Manipulators for output streams: #include <iomanip.h>
declares: manipulators setbase(base) (8, 10, 16)
setfill(fillChar)
setw(width)
-- and many others
NB: setw applies only to the NEXT operation on the stream
2. File input/output: #include <fstream.h>
a. automatically includes <iostream.h>
b. declares classes ifstream and ofstream, which inherit from istream
and ostream and thus get all the above methods discussed for
cin or cout:
constructors include
ifstream(char *)
ofstream(char *)
Note: if filename is in a string object, use, the objects
c_str() method to convert it to a C-style string
Ex:
string s;
cout << "Enter input file name: ";
cin >> s;
ifstream infile(s.c_str());
... can now do input from infile using << etc.
3. Treating a C-style string as an input or output stream:
#include <strstream.h>
a. automatically includes <iostream.h>
b. declares classes istrstream and ostrstream, which inherit from
istream and ostream and thus get all the above methods
c. constructors include
istrstream(const char *, int n)
ostrstream(char *, int n)
D. Testing for errors on C++ streams
1. One important question about any input-output package is how errors
are handled (especially those due to malformed input). In some
languages, IO errors are automatically signalled in some way by the
language support system. However, the C++ iostream facility does not
do this.
2. Instead, the class ios (which is the base class of istream and
ostream) maintains a state value which can be good or can record one
or more of the following:
fail - an operation failed due to something like malformed input
bad - an operation failed due to an io system error
eof - (input only) the stream is at end of file - becomes
true only AFTER an operation has been tried and failed
due to this)
3. Further, if the state of the stream is anything other than good, any
operation attempted upon it is ignored. (The operation returns to
the caller, but no io occurs)
4. There are multiple ways of testing the state of a stream, including
the following:
stream.good()
stream.fail() (True if state is fail and/or bad)
stream.bad()
stream.eof()
It is also possible to test the state of the stream using operator !,
which is true iff the stream is not good
5. The method stream.clear() can be used to restore the stream to a good
state after an error
Example
{
int i;
cin >> i;
if (! cin)
{
if (cin.eof()) cout << "eof" << endl;
else if (cin.bad()) cout << "io system error" << endl;
else cout << "malformed input" << endl;
cin.clear();
}
else
cout << "You entered: " << i << endl;
}
II. Overloading iostream operators >>, << for user-defined objects
-- ----------- -------- --------- -- -- --- ------------ -------
A. iostream.h defines >> and << for standard types as methods of classes
istream, ostream, with different signatures - e.g. the declaration
for class istream contains
istream& operator >> (char*);
istream& operator >> (char&);
istream& operator >> (int&);
istream& operator >> (float&);
istream& operator >> (double&);
ostream& operator << (const char *);
ostream& operator << (char);
ostream& operator << (int);
ostream& operator << (float);
ostream& operator << (double);
(and many variations to deal with unsigned, long, short etc. versions of
the above)
B. To define extractor and inserter operators for our own data types, it
would appear we would need to make them methods of classes istream or
ostream. But we don't want to tamper with the standard header files!
Solution: define global operator functions
Example:
class Rational
{
public:
Rational(int numerator, int denominator);
....
// Note that these are global functions declared as friends,
// not class members
friend ostream & operator << (ostream & s, const Rational & r);
friend istream & operator >> (istream & s, Rational & r);
private:
int _numerator,
_denominator;
};
...
ostream & operator << (ostream & s, const Rational & r)
{ s << r._numerator << " / " << r._denominator;
return s; // NEEDED FOR CHAINING
}
istream & operator >> (istream & s, Rational & r)
{ char separator;
s >> r._numerator >> separator >> r._denominator;
if (separator != '/') s.set(ios::failbit);
return s; // NEEDED FOR CHAINING
}
III. Persistent objects
--- ---------- -------
A. What we will discuss here is not a standardized idea - e.g. not in any
way a part of the standard C++ library.
B. A problem: Ordinary C++ objects reside in memory, and hence "live" only
during the run of a program. No way for objects to be
preserved between runs of a program.
C. A Solution: Create persistent objects that reside on disk, and hence
"live" permanently. When program is stopped and restarted,
these objects are still there.
Example: what objects in video store need persistence?
D. Methodology: The book discusses one approach to this. The basic idea is
for each class to include an "archive" method which can be
used either to save an object of that class to disk or to
restore an object from disk. (Saving/restoring an object
entails recursively saving/restoring other objects it
contains or has pointers/references to.) At program
termination, this method is invoked on the top-level object
(e.g. the store itself), resulting in all objects being
saved to disk; when the program is started up again, this
method is invoked to restore the top-level objects,
which in turn restores all other objects.
Note: the Video Store project as distribute incorporates this.
E. Distributed objects
1. Carrying the idea of persistence one step further leads to software in
which the objects involved may be distributed over a network.
Individual application programs may start and stop, but the major
objects they use have an ongoing existence. In this case "sending a
message to an object" may mean quite literally that.
2. There is an emerging common industry standard for allowing objects
implemented in a variety of different languages on a variety of
different platforms to inter-operate with one another. The standard
is known as CORBA - Common Object Request Broker Architecture.
Copyright ©1999 - Russell C. Bjork