G
goodbyeera
Before starting with my question, I now first typed 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 t
he end of the scope in which its stream was declared.
This raises the question of how an implementation can ensure that the
predef
ined streams cout, cin, cerr, and clog are created before their first
use an
d closed (only) after their last use. Naturally, different
implementations o
f 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
gener
al 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 keep
s 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
eachfil
e #including <iostream>
int ios_base::Init::count = 0; // in some .c file
Each translation unit ($9.1) declares its own object called __ioinit.
The c
onstructor for the __ioinit objects uses ios_base::Init::count as a
first-
time switch to ensure that actual initialization of the global objects
of th
e 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::cou
nt 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
initiali
zation 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 s
witch in the functions that read and write single characters would
have been
quite noticeable.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ÎÒ²»ÊǺÜÃ÷°×ÒÔÉϵÄ×ö·¨ÈçºÎ´ïµ½"ensure that the predefined streams cout, cin,
cerr, and clog are created before their first use and closed (only)
after t
heir last use"µÄÄ¿µÄ¡£ÎÒ¶ÔÕâ¾ä»°µÄÀí½âÊÇ£¬Òª±£Ö¤µ±Ä³¸öÈ«¾Ö±äÁ¿µÄ¹¹Ô캯ÊýÓõ½
coutµÄʱºò£¬ËüÒѾ±»¹¹ÔìºÃÁË£¬ÒÔ¼°µ±Ä³¸öÈ«¾Ö±äÁ¿µÄÎö¹¹º¯ÊýÓõ½coutµÄʱºò£¬Ëü
»¹Ã»Óб»Îö¹¹¡£¸Ð¾õÕâµÃ¿¿implementationÓÃijЩÌØÊâµÄ°ì·¨²ÅÄÜ×öµ½£¬ÒòΪһ°ãÀ´½²²»
ͬcompilation unitµÄÈ«¾Ö±äÁ¿µÄ¹¹Ôì˳ÐòÊDz»È·¶¨µÄ¡£ÁíÍ⣬ÔÎÄÖÐ˵ÔÚios_base::
Init::Init()ÖÐinitialize cout£¬µ«ÊÇcoutµÄÀàÐÍÊÇbasic_ostream<char>£¬ËüÖ»ÓÐΨ
Ò»µÄÒ»ÖÖ¹¹Ô캯Êý£¬¹¹ÔìÍêÁËÖ®ºóҲûÓбðµÄʲôº¯ÊýÔÙ¿ÉÒÔ³õʼ»¯£¨²»Ïñbasic_ofstre
am£¬¿ÉÒÔÏÈÓÃĬÈϹ¹Ô죬֮ºóÔÙµ÷ÓÃopen()º¯ÊýÀ´³õʼ»¯£©£¬ËùÒÔÎÒ²»ÊǺÜÃ÷°×ÔÎÄÖÐ
µÄinitialize coutÖ¸µÄÊÇʲô¡£
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 t
heir 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 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 t
he end of the scope in which its stream was declared.
This raises the question of how an implementation can ensure that the
predef
ined streams cout, cin, cerr, and clog are created before their first
use an
d closed (only) after their last use. Naturally, different
implementations o
f 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
gener
al 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 keep
s 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
eachfil
e #including <iostream>
int ios_base::Init::count = 0; // in some .c file
Each translation unit ($9.1) declares its own object called __ioinit.
The c
onstructor for the __ioinit objects uses ios_base::Init::count as a
first-
time switch to ensure that actual initialization of the global objects
of th
e 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::cou
nt 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
initiali
zation 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 s
witch in the functions that read and write single characters would
have been
quite noticeable.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ÎÒ²»ÊǺÜÃ÷°×ÒÔÉϵÄ×ö·¨ÈçºÎ´ïµ½"ensure that the predefined streams cout, cin,
cerr, and clog are created before their first use and closed (only)
after t
heir last use"µÄÄ¿µÄ¡£ÎÒ¶ÔÕâ¾ä»°µÄÀí½âÊÇ£¬Òª±£Ö¤µ±Ä³¸öÈ«¾Ö±äÁ¿µÄ¹¹Ô캯ÊýÓõ½
coutµÄʱºò£¬ËüÒѾ±»¹¹ÔìºÃÁË£¬ÒÔ¼°µ±Ä³¸öÈ«¾Ö±äÁ¿µÄÎö¹¹º¯ÊýÓõ½coutµÄʱºò£¬Ëü
»¹Ã»Óб»Îö¹¹¡£¸Ð¾õÕâµÃ¿¿implementationÓÃijЩÌØÊâµÄ°ì·¨²ÅÄÜ×öµ½£¬ÒòΪһ°ãÀ´½²²»
ͬcompilation unitµÄÈ«¾Ö±äÁ¿µÄ¹¹Ôì˳ÐòÊDz»È·¶¨µÄ¡£ÁíÍ⣬ÔÎÄÖÐ˵ÔÚios_base::
Init::Init()ÖÐinitialize cout£¬µ«ÊÇcoutµÄÀàÐÍÊÇbasic_ostream<char>£¬ËüÖ»ÓÐΨ
Ò»µÄÒ»ÖÖ¹¹Ô캯Êý£¬¹¹ÔìÍêÁËÖ®ºóҲûÓбðµÄʲôº¯ÊýÔÙ¿ÉÒÔ³õʼ»¯£¨²»Ïñbasic_ofstre
am£¬¿ÉÒÔÏÈÓÃĬÈϹ¹Ô죬֮ºóÔÙµ÷ÓÃopen()º¯ÊýÀ´³õʼ»¯£©£¬ËùÒÔÎÒ²»ÊǺÜÃ÷°×ÔÎÄÖÐ
µÄinitialize coutÖ¸µÄÊÇʲô¡£
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 t
heir 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 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.