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

📄 mc5.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML>
<HEAD>
<TITLE>More Effective C++ | Chapter 5: Techniques</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 21; setCurrentMax(21);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var dingbase = "MC5_DIR.HTM"; var dingtext = "MEC++ Techniques, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">
<!-- SectionName="MEC++ Chapter Intro: Techniques" -->
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC4_FR.HTM#41284" TARGET="_top" onMouseOver = "self.status ='Back to MEC++ Item 24'; return true" onMouseOut = "self.status = self.defaultStatus">Item 24: Understand the costs of virtual functions, multiple inheritance, virtual base classes, and RTTI</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MC5.HTM#5341" onMouseOver = "self.status ='Item 25: Virtualizing constructors and non-member functions.'; return true" onMouseOut = "self.status = self.defaultStatus">Item 25: Virtualizing constructors and non-member functions.</A></FONT></DIV>

<A NAME="6181"></A><A NAME="p123"></A><A NAME="6056"></A>
<P><A NAME="dingp1"></A><font ID="mgtitle">Techniques</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="6057"></A>
<P><A NAME="dingp2"></A>
Most of this book is concerned with programming guidelines. Such guidelines are important, but no programmer lives by guidelines alone. According to the old TV show <I>Felix the Cat</I>, "Whenever he gets in a fix, he reaches into his bag of tricks." Well, if a cartoon character can have a bag of tricks, so too can C++ programmers. Think of this chapter as a starter set for your bag of <NOBR>tricks.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P><A NAME="26219"></A>
<P><A NAME="dingp3"></A>
Some problems crop up repeatedly when designing C++ software. How can you make constructors and non-member functions act like virtual functions? How can you limit the number of instances of a class? How can you prevent objects from being created on the heap? How can you guarantee that they will be created there? How can you create objects that automatically perform some actions anytime some other class's member functions are called? How can you have different objects share data structures while giving clients the illusion that each has its own copy? How can you distinguish between read and write usage of <CODE>operator[]</CODE>? How can you create a virtual function whose behavior depends on the <A HREF="./MCINTRFR.HTM#72671" TARGET="_top" onMouseOver = "self.status = 'Link to dynamic type'; return true" onMouseOut = "self.status = self.defaultStatus">dynamic type</a>s of more than one <NOBR>object?<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P><A NAME="26353"></A>
<P><A NAME="dingp4"></A>
All these questions (and more) are answered in this chapter, in which I describe proven solutions to problems commonly encountered by C++ programmers. I call such solutions <I>techniques</I>, but they're also known as <I>idioms</I> and, when documented in a stylized fashion, <I>patterns</I>. Regardless of what you call them, the information that follows will serve you well as you engage in the day-to-day skirmishes of practical software development. It should also convince you that no matter what you want to do, there is almost certainly a way to do it in <NOBR>C++.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></p>

<!-- SectionName="M25: Virtualizing ctors and non-member functions" -->
<A NAME="5341"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#6056">Techniques</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="#5350">Item 26: Limiting the number of objects of a class</A></FONT></DIV>
<P><A NAME="dingp5"></A><font ID="mititle">Item 25: &nbsp;Virtualizing constructors and non-member functions.</font><SCRIPT>create_link(5);</SCRIPT>
</P>

<A NAME="72164"></A><A NAME="11237"></A>

<P><A NAME="dingp6"></A>
On the face of it, it doesn't make much sense to talk about "virtual constructors." You call a virtual function to achieve type-specific behavior <A NAME="p124"></A>when you have a pointer or reference to an object but you don't know what the real type of the object is. You call a constructor only when you don't yet have an object but you know exactly what type you'd like to have. How, then, can one talk of <I>virtual</I> <NOBR>constructors?<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P><A NAME="11236"></A>
<P><A NAME="dingp7"></A>
It's easy. Though virtual constructors may seem nonsensical, they are remarkably useful. (If you think nonsensical ideas are never useful, how do you explain the success of modern physics?) For example, suppose you write applications for working with newsletters, where a newsletter consists of components that are either textual or graphical. You might organize things this <NOBR>way:<SCRIPT>create_link(7);</SCRIPT>
</NOBR></p>
<A NAME="61094"></A>
<UL><PRE>
class NLComponent {               // abstract base class for
public:                           // newsletter components
<A NAME="61124"></A>
  ...                             // contains at least one
};                                // pure virtual function
<A NAME="61095"></A>
class TextBlock: public NLComponent {
public:
  ...                             // contains no pure virtual
};                                // functions
<A NAME="62249"></A>
class Graphic: public NLComponent {
public:
  ...                             // contains no pure virtual
};                                // functions
<A NAME="70590"></A>
class NewsLetter {                // a newsletter object
public:                           // consists of a list of
  ...                             // NLComponent objects
<A NAME="70591"></A>
private:
  list&lt;NLComponent*&gt; components;
};
</PRE>
</UL>

<A NAME="61229"></A>
<P><A NAME="dingp8"></A>
The classes relate in this <NOBR>way:<SCRIPT>create_link(8);</SCRIPT>
</NOBR></P>
<BR><BR>
<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_124A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_124A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_124A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_124A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_124A5.GIF" BORDER=0></SPAN>

<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_124A5.GIF" BORDER=0></SPAN>
<BR><BR>

<A NAME="61228"></A>
<P><A NAME="dingp9"></A>
The <CODE>list</CODE> class used inside <CODE>NewsLetter</CODE> is part of the Standard Template Library, which is part of the standard C++ library (see <A HREF="../EC/EC7_FR.HTM#8392" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 49'; return true" onMouseOut = "self.status = self.defaultStatus">Item E49</A> <A NAME="p125"></A>and <A HREF="./MC6_FR.HTM#5473" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 35'; return true" onMouseOut = "self.status = self.defaultStatus">Item 35</A>). Objects of type <CODE>list</CODE> behave like doubly linked lists, though they need not be implemented in that <NOBR>way.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P><A NAME="61274"></A>
<P><A NAME="dingp10"></A>
<CODE>NewsLetter</CODE> objects, when not being worked on, would likely be stored on disk. To support the creation of a <CODE>Newsletter</CODE> from its on-disk representation, it would be convenient to give <CODE>NewsLetter</CODE> a constructor that takes an <CODE>istream</CODE>. The constructor would read information from the stream as it created the necessary in-core data <NOBR>structures:<SCRIPT>create_link(10);</SCRIPT>
</NOBR></p>

<A NAME="61287"></A>
<UL><PRE>class NewsLetter {
public:
  NewsLetter(istream&amp; str);
  ...</PRE>
</UL>

<A NAME="61299"></A>
<P><A NAME="dingp11"></A>
Pseudocode for this constructor might look like <NOBR>this,<SCRIPT>create_link(11);</SCRIPT>
</NOBR></p>

<A NAME="61300"></A>
<UL><PRE>NewsLetter::NewsLetter(istream&amp; str)
{
  while (str) {
    <I>read the next component object from str;</I>
<A NAME="61308"></A>
    <I>add the object to the list of this
    newsletter's components;</I>
  }
}
</PRE>
</UL>

<A NAME="61302"></A><p><A NAME="dingp12"></A>
or, after moving the tricky stuff into a separate function called <CODE>readComponent</CODE>, like <NOBR>this:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></p>

<A NAME="61390"></A>
<UL><PRE>class NewsLetter {
public:
  ...
<A NAME="61397"></A>
private:
  // read the data for the next NLComponent from str,
  // create the component and return a pointer to it
  static NLComponent * readComponent(istream&amp; str);
  ...
};
<A NAME="61342"></A>
NewsLetter::NewsLetter(istream&amp; str)
{
  while (str) {
    // add the pointer returned by readComponent to the
    // end of the components list; "push_back" is a list
    // member function that inserts at the end of the list
    components.push_back(readComponent(str));
  }
}
</PRE>
</UL>

<A NAME="61328"></A>
<P><A NAME="dingp13"></A>
Consider what <CODE>readComponent</CODE> does. It creates a new object, either a <CODE>TextBlock</CODE> or a <CODE>Graphic</CODE>, depending on the data it reads. Because it <A NAME="p126"></A>creates new objects, it acts much like a constructor, but because it can create different types of objects, we call it a <I>virtual constructor</I>. A virtual constructor is a function that creates different types of objects depending on the input it is given. Virtual constructors are useful in many contexts, only one of which is reading object information from disk (or off a network connection or from a tape, <NOBR>etc.).<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P><A NAME="61377"></A>
<P><A NAME="dingp14"></A>
A particular kind of virtual constructor &#151; the <I>virtual copy constructor</I> &#151; is also widely useful. A virtual copy constructor returns a pointer to a new copy of the object invoking the function. Because of this behavior, virtual copy constructors are typically given names like <CODE>copySelf</CODE>, <CODE>cloneSelf</CODE>, or, as shown below, just plain <CODE>clone</CODE>. Few functions are implemented in a more straightforward <NOBR>manner:<SCRIPT>create_link(14);</SCRIPT>
</NOBR></p>
<A NAME="61421"></A>
<UL><PRE>class NLComponent {
public:
  // declaration of virtual copy constructor
  virtual NLComponent * clone() const = 0;
  ...
<A NAME="75507"></A>
};
<A NAME="61423"></A>
class TextBlock: public NLComponent {
public:
  virtual TextBlock * clone() const         // virtual copy
  { return new TextBlock(*this); }          // constructor
  ...
<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="dingp15"></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="#6073" onMouseOver = "self.status = 'Link to MEC++ Item 29'; return true" onMouseOut = "self.status = self.defaultStatus">Item 29</A>), so does the virtual copy constructor. Consistency &#151; what a wonderful <NOBR>thing.<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P><A NAME="61453"></A>
<P><A NAME="dingp16"></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(16);</SCRIPT>
</P><A NAME="61463"></A>
<P><A NAME="dingp17"></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(17);</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

⌨️ 快捷键说明

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