⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 str_0182.htm

📁 C++标准库 C++标准库 C++标准库 C++标准库
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<HTML><HEAD><TITLE>2.9 Streams and Stream Buffers</TITLE></HEAD><BODY><A HREF="ug2.htm"><IMG SRC="images/banner.gif"></A><BR><A HREF="man_6665.htm"><IMG SRC="images/prev.gif"></A><A HREF="booktoc2.htm"><IMG SRC="images/toc.gif"></A><A HREF="syn_4687.htm"><IMG SRC="images/next.gif"></A><BR><STRONG>Click on the banner to return to the user guide home page.</STRONG><H2>2.9 Streams and Stream Buffers</H2><P>So far we have used streams as the source or target of input and output operations.  But there is another aspect of streams:  they are also objects in your C++ program that you might want to copy and pass around like other objects.  However, streams cannot simply be copied and assigned.  The following section shows what you have to do instead.</P><P>Stream buffers play a special role.  In numerous cases you will not want to create copies of a stream buffer object, but simply share a stream buffer among streams.  <A HREF="str_0182.htm#2.9.2">Section 2.9.2</A> explores this option.</P><P>If you really want to work with copies of stream buffers, see <A HREF="str_0182.htm#2.9.3">section 2.9.3</A> for hints and caveats.</P><A NAME="2.9.1"><H3>2.9.1 Copying and Assigning Stream Objects</H3></A><P>Stream objects cannot simply be copied and assigned.  Let us consider a practical example to see what it means.  A program writes data to a file if a file name is specified on program call, or to the standard output stream <SAMP>cout</SAMP> if no file name is specified.  You want to write to one output stream in your program; this stream will either be a file stream or the standard output stream.  The most obvious way to do this is to declare an output file stream object and assign it to <SAMP>cout</SAMP>, or to use <SAMP>cout</SAMP> directly.  However, you can't do it this way:</P><PRE>int main(int argc, char argv[]){  ofstream fil;  if (argc > 1)  {  fil.open(argv[1]);     cout = fil;                            // never do this !!!  }   // output to cout, e.g.   cout &#60;&#60; "Hello world!" &#60;&#60; endl;}</PRE><P>This solution is bad for at least two reasons.  First, assignments to any of the predefined streams should be avoided.  The predefined stream <SAMP>cin</SAMP>, <SAMP>cout</SAMP>, <SAMP>cerr</SAMP>, and <SAMP>clog</SAMP> have special properties and are treated differently from other streams.  If you reassign them, as done with <SAMP>cout</SAMP> in the example above, you lose their special properties.  Second, assignment and copying of streams is hazardous.  The assignment of the output stream <SAMP>fil</SAMP> will compile and might even work; however, your program is likely to crash afterwards. </FN><HR><STRONG><P><I>Please note: </I> Stream objects must never be copied or assigned to each other.</P></STRONG><HR><P>Let us see why.  The copy constructor and the assignment operator for streams are not defined; therefore, the compiler-generated default copy constructor and assignment operator are used.  As usual, they only copy and assign a stream object's data members, which is insufficient because a stream object holds pointers as well.  Figure 14 is a sketch of the data held by a stream object:</FN><H4>Figure 14.  Data held by a stream object.  Please note that some data members are omitted for the sake of simplicity.</H4><BR><IMG SRC="images/image31.gif"><P>The stream buffer contains several pointers to the actual character buffer it maintains.  The default copy constructor and assignment operator will not correctly handle these pointers.</P><A NAME="2.9.1.1"><H4>2.9.1.1 Copying a Stream's Data Members</H4></A><P>To achieve the equivalent effect of a copy, you might consider copying each data member individually.  This can be done as in the following example:</P><PRE>int main(int argc, char argv[]){  ofstream out;  if (argc > 1)     out.open(argv[1]);  else  {  out.copyfmt(cout);                                       //1     out.clear(cout.rdstate());                               //2     out.rdbuf(cout.rdbuf());                                 //3  }   // output to out, e.g.   out &#60;&#60; "Hello world!" &#60;&#60; endl;}</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>The <SAMP>copyfmt()</SAMP> function copies all data from the standard output stream <SAMP>cout</SAMP> to the output file stream <SAMP>out</SAMP>, except the error state and the stream buffer.  (There is a function <SAMP>exceptions()</SAMP> that allows you to copy the exception mask separately; i.e., <SAMP>cout.exceptions(fil.exceptions());</SAMP>. However, you need not do this explicitly, since <SAMP>copyfmt()</SAMP> already copies the exception mask.)</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>Here the error state is copied.</TD></TR><TR VALIGN="top"><TD>//3</TD><TD>Here the stream buffer pointer is copied.</TD></TR></TABLE><P>Please note the little snag here.  After the call to <SAMP>rdbuf()</SAMP>, the buffer is shared between the two streams, as shown in Figure 15:</P><H4>Figure 15.  Copying a stream's internal data results in a shared buffer</H4><BR><IMG SRC="images/image32.gif"><A NAME="2.9.1.2"><H4>2.9.1.2 Sharing Stream Buffers Inadvertently</H4></A><P>Whether or not you intend to share a stream buffer among streams depends on your application.  In any case, it is important that you realize the stream buffer is shared after a call to <SAMP>rdbuf()</SAMP>; in other words, you must monitor the lifetime of the stream buffer object and make sure it exceeds the lifetime of the stream.  In our little example above, we use the standard output stream's buffer.  Since the standard streams are static objects, their stream buffers have longer lifetimes that most other objects, so we are safe.  However, whenever you share a stream buffer among other stream objects, you must carefully consider the stream buffer's lifetime.</P><P>The example above has another disadvantage we haven't considered yet, as shown in the following code:</P><PRE>int main(int argc, char argv[]){  ofstream out;  if (argc > 1)     out.open(argv[1]);  else  {  out.copyfmt(cout);                                       //1     out.clear(cout.rdstate());                               //2     out.rdbuf(cout.rdbuf());                                 //3  }   // output to out, e.g.   out &#60;&#60; "Hello world!" &#60;&#60; endl;}</PRE><P>As we copy the standard output stream's entire internal data, we also copy its special behavior.  For instance, the standard output stream is synchronized with the standard input stream.  (See <A HREF="how_0298.htm">Section 2.1</A>0.4 for further details.)  If our output file stream <SAMP>out</SAMP> is a copy of <SAMP>cout</SAMP>, it is forced to synchronize its output operations with all input operations from <SAMP>cin</SAMP>.  This might not be desired, especially since synchronization is a time-consuming activity.  Here is a more efficient approach using only the stream buffer of the standard output stream:</P><PRE>int main(int argc, char argv[]){  filebuf* fb = new filebuf;                                  //1  ostream out((argc>1)?                                       //2    fb->open(argv[1],ios_base::out|ios_base::trunc):          //3    cout.rdbuf());                                            //4  if (out.rdbuf() != fb)    delete fb;  out &#60;&#60; "Hello world!" &#60;&#60; endl;}</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>Instead of creating a file stream object, which already contains a file buffer object, we construct a separate file buffer object on the heap that we can hand over to an output stream object if needed.  This way we can delete the file buffer object if not needed.  In the original example, we constructed a file stream object with no chance of eliminating the file buffer object if not used. </TD></TR><TR VALIGN="top"><TD>//2</TD><TD>An output stream is constructed.  The stream has either the standard output stream's buffer, or a file buffer connected to a file.</TD></TR><TR VALIGN="top"><TD>//3</TD><TD>If the program is provided with a file name, the file is opened and connected to the file buffer object.  (Note that you must ensure that the lifetime of this stream buffer object exceeds the lifetime of the output stream that uses it.)  The <SAMP>open()</SAMP> function returns a pointer to the file buffer object.  This pointer is used to construct the output stream object.</TD></TR><TR VALIGN="top"><TD>//4</TD><TD>If no file name is provided, the standard output stream's buffer is used.</TD></TR></TABLE><P>As in the original example, <SAMP>out</SAMP> inserts through the standard output stream's buffer, but lacks the special properties of a standard stream.  </P><P>Here is an alternative solution that uses file descriptors, a non-standard feature of Rogue Wave's implementation of the standard iostreams:</FN><PRE>int main(int argc, char argv[]){  ofstream out;  if (argc > 1)     out.open(argv[1]);                        //1  else              out.rdbuf()->open(1);                     //2  out &#60;&#60; "Hello world!" &#60;&#60; endl;}</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>If the program is provided with a file name, the file is opened and connected to the file buffer object.</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>Otherwise, the output stream's file buffer is connected to the standard input stream <SAMP>stdout</SAMP> whose file descriptor is <SAMP>1</SAMP>.</TD></TR></TABLE><P>The effect is the same as in the previous solution, because the standard output stream <SAMP>cout</SAMP> is connected to the C standard input file <SAMP>stdout</SAMP>.  This is the simplest of all solutions, because it doesn't involve reassigning or sharing stream buffers.  The output file stream's buffer is simply connected to the right file.  However, this is a non-standard solution, and may decrease portability.</P><A NAME="2.9.1.3"><H4>2.9.1.3 Using Pointer or References to Streams</H4></A><P>If you do not want to deal with stream buffers at all, you can also use pointers or references to streams instead.  Here is an example:</P><PRE>int main(int argc, char argv[]){  ostream* fp;                                                //1  if (argc > 1)      fp = new ofstream(argv[1]);                             //2  else     fp = &#38;cout                                               //3  // output to *fp, e.g.   *fp &#60;&#60; "Hello world!" &#60;&#60; endl;                              //4  if(fp!=&#38;cout) delete fp;}</PRE><TABLE CELLPADDING="3"><TR VALIGN="top"><TD>//1</TD><TD>A pointer to an <SAMP>ostream</SAMP> is used.  (Note that it cannot be a pointer to an <SAMP>ofstream</SAMP>, because the standard output stream <SAMP>cout</SAMP> is not a file stream, but a plain stream of type <SAMP>ostream</SAMP>.)</TD></TR><TR VALIGN="top"><TD>//2</TD><TD>A file stream for the named output file is created on the heap and assigned to the pointer, in case a file name is provided.</TD></TR><TR VALIGN="top"><TD>//3</TD><TD>Otherwise, a pointer to <SAMP>cout</SAMP> is used.</TD></TR><TR VALIGN="top"><TD>//4</TD><TD>Output is written through the pointer to either <SAMP>cout</SAMP> or the named output file.</TD></TR></TABLE><P>An alternative approach could use a reference instead of a pointer:</P><PRE>int main(int argc, char argv[]){  ostream&#38; fr = (argc > 1) ? *(new ofstream(argv[1])) : cout;  // output to *fr, e.g.   fr &#60;&#60; "Hello world!" &#60;&#60; endl;  if (&#38;fr!=&#38;cout) delete(&#38;fr);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -