G
goodbyeera
Before starting with my question, I now first type in the original
section from TC++PL as below:
21.5.2 Closing of Streams
A file can be explicitly closed by calling close() on its stream:
void f(ostream& mystream)
{
// ...
mystream.close();
}
However, this is implicitly done by the stream's destructor. So an
explicit call of close() is needed only if the file must be closed
before reaching the end of the scope in which its stream was
declared.
This raises the question of how an implementation can ensure that the
predefined streams cout, cin, cerr, and clog are created before their
first use and closed (only) after their last use. Naturally, different
implementations of the <iostream> stream library can use different
techniques to achieve this . After all, exactly how it is done is an
implementation detail that should not be visible to the user. Here, I
present just one technique that is general enough to be used to ensure
proper order of construction and destruction of global objects of a
variety of types. An implementation may be able to do better by taking
advantage of special features of a compiler or linker.
The fundamental idea is to define a helper class that is a counter
that keeps track of how many times <iostream> has been included in a
separately compiled source file:
class ios_base::Init {
static int count;
public:
Init();
~Init();
} ;
namespace { ios_base::Init __ioinit; } // in <iostream>, one copy in
eachfile #including <iostream>
int ios_base::Init::count = 0; // in some .c file
Each translation unit ($9.1) declares its own object called __ioinit.
The constructor for the __ioinit objects uses ios_base::Init::count as
a first-time switch to ensure that actual initialization of the global
objects of the stream I/O library is done exactly once:
ios_base::Init::Init() { if (count++ == 0) { /* initialize cout, cerr,
cin, etc. */ } }
Conversely, the destructor for the __ioinit objects uses
ios_base::Init::count as a last-time switch to ensure that the streams
are closed:
ios_base::Init::~Init() { if (--count == 0) { /* clean up cout (flush,
etc.) , cerr, cin, etc. */ } }
This is a general technique for dealing with libraries that require
initialization and cleanup of global objects. In a system in which all
code resides in main memory during execution, the technique is almost
free. When that is not the case, the overhead of bringing each object
file into main memory to execute its initialization function can be
noticeable. When possible, it is better to avoid global objects. For a
class in which each operation performs significant work, it can be
reasonable to test a first-time switch (like ios_base::Init::count) in
each operation to ensure initialization. However, that approach would
have been prohibitively expensive for streams. The overhead of a first-
time switch in the functions that read and write single characters
would have been quite noticeable.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I don't understand very much about how the above technique "ensure
that the predefined streams cout, cin, cerr, and clog are created
before their first use and closed (only)
after their last use". My understanding to this quoted sentence is
that, we
need to make sure, when a global object uses cout in its constructor,
cout should already be constructed by the time; and when a global
object uses cout in its destructor, cout must not be destructed yet
by
the time. It seems that this can be achieved only by some
implementation-defined special technique, since the construction
sequence of global objects from different compilation unit is
unspecified.
Also, the text says that, in ios_base::Init::Init(), it will
initialize cout. But cout is of type basic_ostream<char>, and it has
only one form of constructor which takes a pointer to a
basic_streambuf, and doesn't have any member other function to re-
initialize
it after construction (while in the case for basic_ofstream, it can
be
constructed using a default constructor, and then be called with the
member function open() later to get initialized). So I don't
understand very much about what the "initialize cout" means in the
above text.
section from TC++PL as below:
21.5.2 Closing of Streams
A file can be explicitly closed by calling close() on its stream:
void f(ostream& mystream)
{
// ...
mystream.close();
}
However, this is implicitly done by the stream's destructor. So an
explicit call of close() is needed only if the file must be closed
before reaching the end of the scope in which its stream was
declared.
This raises the question of how an implementation can ensure that the
predefined streams cout, cin, cerr, and clog are created before their
first use and closed (only) after their last use. Naturally, different
implementations of the <iostream> stream library can use different
techniques to achieve this . After all, exactly how it is done is an
implementation detail that should not be visible to the user. Here, I
present just one technique that is general enough to be used to ensure
proper order of construction and destruction of global objects of a
variety of types. An implementation may be able to do better by taking
advantage of special features of a compiler or linker.
The fundamental idea is to define a helper class that is a counter
that keeps track of how many times <iostream> has been included in a
separately compiled source file:
class ios_base::Init {
static int count;
public:
Init();
~Init();
} ;
namespace { ios_base::Init __ioinit; } // in <iostream>, one copy in
eachfile #including <iostream>
int ios_base::Init::count = 0; // in some .c file
Each translation unit ($9.1) declares its own object called __ioinit.
The constructor for the __ioinit objects uses ios_base::Init::count as
a first-time switch to ensure that actual initialization of the global
objects of the stream I/O library is done exactly once:
ios_base::Init::Init() { if (count++ == 0) { /* initialize cout, cerr,
cin, etc. */ } }
Conversely, the destructor for the __ioinit objects uses
ios_base::Init::count as a last-time switch to ensure that the streams
are closed:
ios_base::Init::~Init() { if (--count == 0) { /* clean up cout (flush,
etc.) , cerr, cin, etc. */ } }
This is a general technique for dealing with libraries that require
initialization and cleanup of global objects. In a system in which all
code resides in main memory during execution, the technique is almost
free. When that is not the case, the overhead of bringing each object
file into main memory to execute its initialization function can be
noticeable. When possible, it is better to avoid global objects. For a
class in which each operation performs significant work, it can be
reasonable to test a first-time switch (like ios_base::Init::count) in
each operation to ensure initialization. However, that approach would
have been prohibitively expensive for streams. The overhead of a first-
time switch in the functions that read and write single characters
would have been quite noticeable.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I don't understand very much about how the above technique "ensure
that the predefined streams cout, cin, cerr, and clog are created
before their first use and closed (only)
after their last use". My understanding to this quoted sentence is
that, we
need to make sure, when a global object uses cout in its constructor,
cout should already be constructed by the time; and when a global
object uses cout in its destructor, cout must not be destructed yet
by
the time. It seems that this can be achieved only by some
implementation-defined special technique, since the construction
sequence of global objects from different compilation unit is
unspecified.
Also, the text says that, in ios_base::Init::Init(), it will
initialize cout. But cout is of type basic_ostream<char>, and it has
only one form of constructor which takes a pointer to a
basic_streambuf, and doesn't have any member other function to re-
initialize
it after construction (while in the case for basic_ofstream, it can
be
constructed using a default constructor, and then be called with the
member function open() later to get initialized). So I don't
understand very much about what the "initialize cout" means in the
above text.