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

📄 mi25.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<A NAME="61449"></A>
};
<A NAME="61425"></A>
class Graphic: public NLComponent {
public:
  virtual Graphic * clone() const            // virtual copy
  { return new Graphic(*this); }             // constructor
  ...
<A NAME="61450"></A>
};
</PRE>
</UL>

<A NAME="61383"></A>
<P><A NAME="dingp11"></A>
As you can see, a class's virtual copy constructor just calls its real copy constructor. The meaning of "copy" is hence the same for both functions. If the real copy constructor performs a shallow copy, so does the virtual copy constructor. If the real copy constructor performs a deep copy, so does the virtual copy constructor. If the real copy constructor does something fancy like reference counting or copy-on-write (see <A HREF="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>), so does the virtual copy constructor. Consistency &#151; what a wonderful <NOBR>thing.<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P><A NAME="61453"></A>
<P><A NAME="dingp12"></A>
Notice that the above implementation takes advantage of a relaxation in the rules for virtual function return types that was adopted relatively recently. No longer must a derived class's redefinition of a base class's virtual function declare the same return type. Instead, if the <A NAME="p127"></A>function's return type is a pointer (or a reference) to a base class, the derived class's function may return a pointer (or reference) to a class derived from that base class. This opens no holes in C++'s type system, and it makes it possible to accurately declare functions such as virtual copy constructors. That's why <CODE>TextBlock</CODE>'s <CODE>clone</CODE> can return a <CODE>TextBlock*</CODE> and <CODE>Graphic</CODE>'s <CODE>clone</CODE> can return a <CODE>Graphic*</CODE>, even though the return type of <CODE>NLComponent</CODE>'s <CODE>clone</CODE> is <CODE>NLComponent*</CODE>.<SCRIPT>create_link(12);</SCRIPT>
</P><A NAME="61463"></A>
<P><A NAME="dingp13"></A>
The existence of a virtual copy constructor in <CODE>NLComponent</CODE> makes it easy to implement a (normal) copy constructor for <CODE>NewsLetter</CODE>:<SCRIPT>create_link(13);</SCRIPT>
</P>

<A NAME="61466"></A>
<UL><PRE>class NewsLetter {
public:
  NewsLetter(const NewsLetter&amp; rhs);
  ...
<A NAME="61473"></A>
private:
  list&lt;NLComponent*&gt; components;
};
<A NAME="61471"></A>
NewsLetter::NewsLetter(const NewsLetter&amp; rhs)
{
  // iterate over rhs's list, using each element's
  // virtual copy constructor to copy the element into
  // the components list for this object. For details on
  // how the following code works, see <a href="./MI35_FR.HTM#5473" TARGET="_top">Item 35</A>.
  for (list&lt;NLComponent*&gt;::const_iterator it =
          rhs.components.begin();
       it != rhs.components.end();
       ++it) {
<A NAME="61482"></A>
    // "it" points to the current element of rhs.components,
    // so call that element's clone function to get a copy
    // of the element, and add that copy to the end of
    // this object's list of components
    components.push_back((*it)-&gt;clone());
  }
}
</PRE>
</UL>

<A NAME="61486"></A>
<P><A NAME="dingp14"></A>
Unless you are familiar with the Standard Template Library, this code looks bizarre, I know, but the idea is simple: just iterate over the list of components for the <CODE>NewsLetter</CODE> object being copied, and for each component in the list, call its virtual copy constructor. We need a virtual copy constructor here, because the list contains pointers to <CODE>NLComponent</CODE> objects, but we know each pointer really points to a <CODE>TextBlock</CODE> or a <CODE>Graphic</CODE>. We want to copy whatever the pointer really points to, and the virtual copy constructor does that for <NOBR>us.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>

<A NAME="p128"></A>
<P><A NAME="dingp15"></A><font ID="mhtitle">Making Non-Member Functions Act Virtual</font><SCRIPT>create_link(15);</SCRIPT>
</P>

<P><A NAME="dingp16"></A><A NAME="61521"></A>Just as constructors can't really be virtual, neither can non-member functions (see <A HREF="../EC/EI19_FR.HTM#5887" TARGET="_top">Item E19</A>). However, just as it makes sense to conceive of functions that construct new objects of different types, it makes sense to conceive of non-member functions whose behavior depends on the <A HREF="./MIINTRFR.HTM#72671" TARGET="_top">dynamic type</a>s of their parameters. For example, suppose you'd like to implement output operators for the <CODE>TextBlock</CODE> and <CODE>Graphic</CODE> classes. The obvious approach to this problem is to make the output operator virtual. However, the output operator is <CODE>operator&lt;&lt;</CODE>, and that function takes an <CODE>ostream&amp;</CODE> as its left-hand argument; that effectively rules out the possibility of making it a member function of the <CODE>TextBlock</CODE> or <CODE>Graphic</CODE> <NOBR>classes.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P><A NAME="61526"></A>
<P><A NAME="dingp17"></A>
(It can be done, but then look what <NOBR>happens:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>

<A NAME="61687"></A>
<UL><PRE>class NLComponent {
public:
  // unconventional declaration of output operator
  virtual ostream&amp; operator&lt;&lt;(ostream&amp; str) const = 0;
  ...
};
<A NAME="61528"></A>
class TextBlock: public NLComponent {
public:
  // virtual output operator (also unconventional)
  virtual ostream&amp; operator&lt;&lt;(ostream&amp; str) const;
};
<A NAME="61567"></A>
class Graphic: public NLComponent {
public:
  // virtual output operator (still unconventional)
  virtual ostream&amp; operator&lt;&lt;(ostream&amp; str) const;
};
<A NAME="61529"></A>
TextBlock t;
Graphic g;
<A NAME="61530"></A>
...
<A NAME="61531"></A>
t &lt;&lt; cout;                                  // print t on cout via
                                            // virtual operator&lt;&lt;; note
                                            // unconventional syntax
<A NAME="61580"></A>
g &lt;&lt; cout;                                  // print g on cout via
                                            // virtual operator&lt;&lt;; note
                                            // unconventional syntax
</PRE>
</UL>

<A NAME="61532"></A>
<P><A NAME="dingp18"></A>
Clients must place the stream object on the <I>right-hand side</I> of the "<CODE>&lt;&lt;</CODE>" symbol, and that's contrary to the convention for output operators. To get back to the normal syntax, we must move <CODE>operator&lt;&lt;</CODE> out of the <CODE>TextBlock</CODE> and <CODE>Graphic</CODE> classes, but if we do that, we can no longer declare it <NOBR>virtual.)<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P><A NAME="61533"></A>
<A NAME="p129"></A>
<P><A NAME="dingp19"></A>
An alternate approach is to declare a virtual function for printing (e.g., <CODE>print</CODE>) and define it for the <CODE>TextBlock</CODE> and <CODE>Graphic</CODE> classes. But if we do that, the syntax for printing <CODE>TextBlock</CODE> and <CODE>Graphic</CODE> objects is inconsistent with that for the other types in the language, all of which rely on <CODE>operator&lt;&lt;</CODE> as their output <NOBR>operator.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P><A NAME="61534"></A>
<P><A NAME="dingp20"></A>
Neither of these solutions is very satisfying. What we want is a non-member function called <CODE>operator&lt;&lt;</CODE> that exhibits the behavior of a virtual function like <CODE>print</CODE>. This description of what we want is in fact very close to a description of how to get it. We define <I>both</I> <CODE>operator&lt;&lt;</CODE> and <CODE>print</CODE> and have the former call the <NOBR>latter!<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>

<A NAME="61535"></A>
<UL><PRE>class NLComponent {
public:
  virtual ostream&amp; print(ostream&amp; s) const = 0;
  ...
<A NAME="75535"></A>
};
<A NAME="61618"></A>
class TextBlock: public NLComponent {
public:
  virtual ostream&amp; print(ostream&amp; s) const;
  ...
<A NAME="75538"></A>
};
<A NAME="61538"></A>
class Graphic: public NLComponent {
public:
  virtual ostream&amp; print(ostream&amp; s) const;
  ...
<A NAME="75541"></A>
};
<A NAME="61540"></A>
inline
ostream&amp; operator&lt;&lt;(ostream&amp; s, const NLComponent&amp; c)
{
  return c.print(s);
}
</PRE>
</UL>

<A NAME="11896"></A>
<P><A NAME="dingp21"></A>
Virtual-acting non-member functions, then, are easy. You write virtual functions to do the work, then write a non-virtual function that does nothing but call the virtual function. To avoid incurring the cost of a function call for this syntactic sleight-of-hand, of course, you inline the non-virtual function (see <A HREF="../EC/EI33_FR.HTM#6729" TARGET="_top">Item E33</A>).<SCRIPT>create_link(21);</SCRIPT>
</P><A NAME="12954"></A>
<P><A NAME="dingp22"></A>
Now that you know how to make non-member functions act virtually on one of their arguments, you may wonder if it's possible to make them act virtually on more than one of their arguments. It is, but it's not easy. How hard is it? Turn to <A HREF="./MI31_FR.HTM#34883" TARGET="_top">Item 31</A>; it's devoted to that <NOBR>question.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MTECH_FR.HTM" TARGET="_top">Techniques</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI26_FR.HTM" TARGET="_top">Item 26: Limiting the number of objects of a class</A></FONT></DIV>

</BODY>
</HTML>

⌨️ 快捷键说明

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