man_6665.htm

来自「C++标准库 C++标准库 C++标准库 C++标准库」· HTM 代码 · 共 438 行 · 第 1/2 页

HTM
438
字号
<UL><LI><P>The manipulator object <SAMP>Manip</SAMP> is an unnamed object;</P><LI><P>The manipulator type <SAMP>manipT</SAMP> is a class; and</P><LI><P>The associated function <IMG SRC="images/inline5.gif"> is a virtual member function of that class.</P></UL><P>The idea here is that the associated function <IMG SRC="images/inline5.gif"> is a non-static member function of the manipulator type  <SAMP>manipT</SAMP>.  In such a model, the manipulator does not store a pointer to the associated function <IMG SRC="images/inline5.gif">, but defines the associated function as a pure virtual member function.  Consequently, the manipulator type <SAMP>manipT</SAMP> will be an abstract class, and concrete manipulator types will be derived from this abstract manipulator type.  They will have to implement the virtual member function that represents the associated function.</P><P>Clearly, we need a new manipulator type because the standard manipulator type <SAMP>smanip </SAMP>is implementation-defined.  In Rogue Wave's <I>Standard C++ Library</I>, it has no virtual member functions, but stores a pointer to the associated function.  Here is the abstract manipulator type we need:</P><PRE>template &#60;class Arg, class Ostream>class virtsmanip{ public:  typedef Arg argument_type;  typedef Ostream ostream_type;  virtsmanip (Arg a) : arg_(a) { }  protected:  virtual Ostream&#38; fct_(Ostream&#38;,Arg) const = 0;  Arg arg_;   friend Ostream&#38;  operator&#60;&#60; (Ostream&#38; ostr             ,const virtsmanip&#60;Arg,Ostream>&#38; manip);};</PRE><P>This type <SAMP>virtsmanip</SAMP> differs from the standard type <SAMP>smanip</SAMP> in several ways:</P><UL><LI><P>It defines the above-mentioned pure virtual member function <SAMP>fct_()</SAMP>.</P></LI><LI><P>The argument <SAMP>arg_</SAMP> and the virtual function <SAMP>fct_()</SAMP> are protected members, and consequently the respective shift operator for the manipulator type has to be a friend function.</P></LI><LI><P>It is a base class for output manipulators only.</P></LI></UL><P>The standard manipulator <SAMP>smanip</SAMP> expects a pointer to a function that takes an <SAMP>ios_base</SAMP> reference.  In this way, a manipulator is always applicable to input <I>and</I> output streams, regardless of whether or not this is intended.  With our new manipulator type <SAMP>virtsmanip,</SAMP> we can define manipulators that cannot inadvertently be applied to input streams.</P><P>Since we have a new manipulator type, we also need a new overloaded version of the manipulator inserter:</P><PRE>template &#60;class Arg, class Ostream>Ostream&#38; operator&#60;&#60; (Ostream&#38; ostr, const virtsmanip&#60;Arg,Ostream>&#38; manip){   manip.fct_(ostr,manip.arg_);   return ostr;}</PRE><P>After these preparations, we can now provide yet another alternative implementation of a manipulator like <SAMP>setprecision()</SAMP>.  This time <SAMP>setprecision()</SAMP> is a manipulator for output streams only:</P><PRE>class setprecision : public virtsmanip&#60;int,basic_ostream&#60;char> > {public: setprecision(argument_type n)  : virtsmanip&#60;argument_type,ostream_type>(n) { } protected: ostream_type&#38; fct_(ostream_type&#38; str, argument_type n) const {   str.precision(n);   return str; }}; </PRE><P><B>Example 4:  Function Object and Static Member Function.</B>  The next example combines (3) and (b), so here:</P></LI></OL><UL><LI><P>The manipulator object <SAMP>Manip</SAMP> is an object of a type <SAMP>M</SAMP> that defines the function call operator;</P></LI><LI><P>The manipulator type <SAMP>manipT</SAMP> is a class type that is returned by the overloaded function call operator of class <SAMP>M</SAMP>; and</P></LI><LI><P>The associated function <IMG SRC="images/inline5.gif"> is a static member function of class <SAMP>M</SAMP>.</P></LI></UL><P>This solution, using a function object as a manipulator, is semantically different from the previous solution in that the manipulator object has <I>state</I>, i.e., it can store data between subsequent uses.</P><P>Let us demonstrate this technique in terms of another example:  an output manipulator that inserts a certain string that is maintained by the manipulator object.  Such a manipulator could be used, for instance, to insert a prefix to each line:</P><PRE>Tag&#60;char> change_mark("v1.2 >> ");while ( new_text )   ostr &#60;&#60; change_mark &#60;&#60; next_line;change_mark("");while ( old_text)   ostr &#60;&#60; change_mark &#60;&#60; next_line;</PRE><P>We would like to derive the <SAMP>Tag</SAMP> manipulator here from the standard manipulator <SAMP>smanip</SAMP>.  Unfortunately, <SAMP>smanip</SAMP> is restricted to associated functions that take an <SAMP>ios_base</SAMP> reference as a parameter.  In our example, we want to insert the stored text to the stream, so we need the stream's inserter.  However, <SAMP>ios_base</SAMP> does not have inserters or extractors.  Consequently we need a new manipulator base type, similar to <SAMP>smanip</SAMP>, that allows associated functions that take a reference to an output stream:</P><PRE>template &#60;class Ostream, class Arg>class osmanip {  public:    typedef Ostream ostream_type;    typedef Arg argument_type;     osmanip(Ostream&#38; (*pf)(Ostream&#38;, Arg), Arg arg)    : pf_(pf) , arg_(arg) { ; }   protected:    Ostream&#38;     (*pf_)(Ostream&#38;, Arg);    Arg          arg_;   friend Ostream&#38;    operator&#60;&#60;     (Ostream&#38; ostr, const osmanip&#60;Ostream,Arg>&#38; manip);};</PRE><P>Then  we need to define the inserter for the new manipulator type <SAMP>osmanip</SAMP>:</P><PRE>template &#60;class Ostream, class Arg>Ostream&#38; operator&#60;&#60; (Ostream&#38; ostr,const osmanip&#60;Ostream,Arg>&#38; manip){   (*manip.pf_)(ostr,manip.arg_);   return ostr;}</PRE><P>Now we define the function object type <SAMP>M</SAMP>, here called <SAMP>Tag</SAMP>:</P><PRE>template &#60;class  charT>class Tag : public osmanip&#60;basic_ostream&#60;charT>, basic_string&#60;charT> >{public: Tag(argument_type a = "")  : osmanip&#60;basic_ostream&#60;charT>, basic_string&#60;charT> >    (fct_, a) { }  osmanip&#60;ostream_type,argument_type>&#38;  operator() (argument_type a)  {    arg_ = a;    return *this;  } private: static ostream_type&#38; fct_ (ostream_type&#38; str, argument_type a) {    return str &#60;&#60; a; }};</PRE><P>Note that the semantics of this type of manipulator differ from the previous ones, and from the standard manipulator <SAMP>setprecision</SAMP>.  The manipulator object has to be explicitly created before it can be used, as shown in the example below:</P><PRE>Tag&#60;char> change_mark("v1.2 >> ");while ( new_text )   ostr &#60;&#60; change_mark &#60;&#60; next_line;change_mark("");while ( old_text)   ostr &#60;&#60; change_mark &#60;&#60; next_line;</PRE><P>This kind of manipulator is more flexible.  In the example above, you can see that the default text is set to <SAMP>"v1.2 >> "</SAMP> when the manipulator is created.  Thereafter you can use the manipulator as a parameterless manipulator and it will remember this text.  You can also use it as a manipulator taking an argument, and provide it with a different argument each time you insert it.</P><P><B>Example 5:  Function Object and Virtual Member Function.</B>  In the previous example, a static member function is used as the associated function.  This has the slight disadvantage that the associated function cannot modify the manipulator's state.  Should modification be necessary, you might consider using a virtual member function instead.</P><P>Our final example here is a manipulator that stores additional data, the previously mentioned <SAMP>lineno</SAMP> manipulator.  It adds the next line number each time it is inserted:</P><PRE>LineNo lineno;while (!cout){   cout &#60;&#60; lineno &#60;&#60; _;}</PRE><P>The manipulator is implemented following the (3) and (b) pattern, i.e.:</P><UL><LI><P>The manipulator object <SAMP>Manip</SAMP> is an object of a type <SAMP>M</SAMP> that defines the function call operator;</P></LI><LI><P>The manipulator type <SAMP>manipT</SAMP> is a class type that is returned by the overloaded function call operator of class <SAMP>M</SAMP>; and </P></LI><LI><P>The associated function <IMG SRC="images/inline5.gif"> is a virtual member function of class <SAMP>M</SAMP>.</P></UL><P>The manipulator object contains a line number that is initialized when the manipulator object is constructed.  Each time the <SAMP>lineno</SAMP> manipulator is inserted, the line number is incremented.</P><P>For the manipulator base type, we use a slightly modified version of the manipulator type <SAMP>osmanip</SAMP> from Example 3.  The changes are necessary because the associated function in this case may not be a constant member function: </P><PRE>emplate &#60;class Arg, class Ostream>class virtsmanip{ public:  typedef Arg argument_type;  typedef Ostream ostream_type;  virtsmanip (Arg a) : arg_(a) { }  protected:  virtual Ostream&#38; fct_(Ostream&#38;,Arg) = 0;  Arg arg_;   friend Ostream&#38;  operator&#60;&#60; (Ostream&#38; ostr             ,virtsmanip&#60;Arg,Ostream>&#38; manip);};template &#60;class Arg,class Ostream>Ostream&#38;operator&#60;&#60; (Ostream&#38; ostr           ,virtsmanip&#60;Arg,Ostream>&#38; manip){   manip.fct_(ostr,manip.arg_);   return ostr;}</PRE><P>The line number manipulator could be implemented like this:</P><PRE>template &#60;class Ostream>class LineNo : public virtsmanip&#60;int,Ostream >{public: LineNo(argument_type n=0)   : virtsmanip&#60;argument_type, ostream_type> (n)   { }  virtsmanip&#60;argument_type,ostream_type>&#38;  operator() (argument_type arg)  {   arg_ = arg;   return *this;  }protected: argument_type lno_; ostream_type&#38; fct_ (ostream_type&#38; str, argument_type n) {    lno_ = (n>0) ? n : lno_;   str &#60;&#60; ++lno_;   arg_ = -1;   return str; }};</PRE><P>Using a virtual member function as the associated manipulator function introduces the overhead of a virtual function call each time the manipulator is inserted.  If it is necessary that a manipulator update its state after each insertion, a static member function will not suffice.  A global function that is a friend of the manipulator type might do the trick.  However, in an object-oriented program, you are usually advised against global functions that modify private or protected data members of a class they are friends with.</P><HR><A HREF="inp_5394.htm"><IMG SRC="images/prev.gif"></A> <A HREF="booktoc2.htm"><IMG SRC="images/toc.gif"></A><A HREF="str_0182.htm"><IMG SRC="images/next.gif"></A><P>&copy;Copyright 1996, Rogue Wave Software, Inc.</P></BODY></HTML>

⌨️ 快捷键说明

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