📄 mc5.htm
字号:
// 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="./MC6_FR.HTM#5473" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 35'; return true" onMouseOut = "self.status = self.defaultStatus">Item 35</A>.
for (list<NLComponent*>::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)->clone());
}
}
</PRE>
</UL>
<A NAME="61486"></A>
<P><A NAME="dingp18"></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(18);</SCRIPT>
</NOBR></P>
<A NAME="p128"></A>
<P><A NAME="dingp19"></A><font ID="mhtitle">Making Non-Member Functions Act Virtual</font><SCRIPT>create_link(19);</SCRIPT>
</P>
<A NAME="61521"></A>
<P><A NAME="dingp20"></A>
Just as constructors can't really be virtual, neither can non-member functions (see <A HREF="../EC/EC4_FR.HTM#5887" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 19'; return true" onMouseOut = "self.status = self.defaultStatus">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="./MCINTRFR.HTM#72671" TARGET="_top" onMouseOver = "self.status = 'Link to dynamic type'; return true" onMouseOut = "self.status = self.defaultStatus">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<<</CODE>, and that function takes an <CODE>ostream&</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(20);</SCRIPT>
</NOBR></P><A NAME="61526"></A>
<P><A NAME="dingp21"></A>
(It can be done, but then look what <NOBR>happens:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="61687"></A>
<UL><PRE>class NLComponent {
public:
// unconventional declaration of output operator
virtual ostream& operator<<(ostream& str) const = 0;
...
};
<A NAME="61528"></A>
class TextBlock: public NLComponent {
public:
// virtual output operator (also unconventional)
virtual ostream& operator<<(ostream& str) const;
};
<A NAME="61567"></A>
class Graphic: public NLComponent {
public:
// virtual output operator (still unconventional)
virtual ostream& operator<<(ostream& str) const;
};
<A NAME="61529"></A>
TextBlock t;
Graphic g;
<A NAME="61530"></A>
...
<A NAME="61531"></A>
t << cout; // print t on cout via
// virtual operator<<; note
// unconventional syntax
<A NAME="61580"></A>
g << cout; // print g on cout via
// virtual operator<<; note
// unconventional syntax
</PRE>
</UL>
<A NAME="61532"></A>
<P><A NAME="dingp22"></A>
Clients must place the stream object on the <I>right-hand side</I> of the "<CODE><<</CODE>" symbol, and that's contrary to the convention for output operators. To get back to the normal syntax, we must move <CODE>operator<<</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(22);</SCRIPT>
</NOBR></P><A NAME="61533"></A>
<A NAME="p129"></A>
<P><A NAME="dingp23"></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<<</CODE> as their output <NOBR>operator.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P><A NAME="61534"></A>
<P><A NAME="dingp24"></A>
Neither of these solutions is very satisfying. What we want is a non-member function called <CODE>operator<<</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<<</CODE> and <CODE>print</CODE> and have the former call the <NOBR>latter!<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="61535"></A>
<UL><PRE>class NLComponent {
public:
virtual ostream& print(ostream& s) const = 0;
...
<A NAME="75535"></A>
};
<A NAME="61618"></A>
class TextBlock: public NLComponent {
public:
virtual ostream& print(ostream& s) const;
...
<A NAME="75538"></A>
};
<A NAME="61538"></A>
class Graphic: public NLComponent {
public:
virtual ostream& print(ostream& s) const;
...
<A NAME="75541"></A>
};
<A NAME="61540"></A>
inline
ostream& operator<<(ostream& s, const NLComponent& c)
{
return c.print(s);
}
</PRE>
</UL>
<A NAME="11896"></A>
<P><A NAME="dingp25"></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/EC5_FR.HTM#6729" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 33'; return true" onMouseOut = "self.status = self.defaultStatus">Item E33</A>).<SCRIPT>create_link(25);</SCRIPT>
</P><A NAME="12954"></A>
<P><A NAME="dingp26"></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="#34883">Item 31</A>; it's devoted to that <NOBR>question.<SCRIPT>create_link(26);</SCRIPT>
</NOBR></P>
<!-- SectionName="M26: Limiting the number of objects of a class" -->
<A NAME="5350"></A><A NAME="p130"></A>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="#5341">Item 25: Virtualizing constructors and non-member functions</A> <BR> Continue to <A HREF="#22627">Item 27: Requiring or prohibiting heap-based objects</A></FONT></DIV>
<P><A NAME="dingp27"></A><font ID="mititle">Item 26: Limiting the number of objects of a class.</font><SCRIPT>create_link(27);</SCRIPT>
</P>
<A NAME="72175"></A>
<P><A NAME="dingp28"></A><A NAME="23500"></A>
Okay, you're crazy about objects, but sometimes you'd like to bound your insanity. For example, you've got only one printer in your system, so you'd like to somehow limit the number of printer objects to one. Or you've got only 16 file descriptors you can hand out, so you've got to make sure there are never more than that many file descriptor objects in existence. How can you do such things? How can you limit the number of <NOBR>objects?<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P> <P><A NAME="dingp29"></A><A NAME="20311"></A>
If this were a proof by mathematical induction, we might start with n = 1, then build from there. Fortunately, this is neither a proof nor an induction. Moreover, it turns out to be instructive to begin with n = 0, so we'll start there instead. How do you prevent objects from being instantiated at <NOBR>all?<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp30"></A><font ID="mhtitle">Allowing Zero or One Objects</font><SCRIPT>create_link(30);</SCRIPT>
</P>
<P><A NAME="dingp31"></A><A NAME="20337"></A>
Each time an object is instantiated, we know one thing for sure: a constructor will be called. That being the case, the easiest way to prevent objects of a particular class from being created is to declare the constructors of that class <NOBR>private:<SCRIPT>create_link(31);</SCRIPT>
</NOBR></P>
<A NAME="20355"></A>
<UL><PRE>class CantBeInstantiated {
private:
CantBeInstantiated();
CantBeInstantiated(const CantBeInstantiated&);
<A NAME="7575"></A>
...
<A NAME="20357"></A>
;
</PRE>
</UL>
<P><A NAME="dingp32"></A><A NAME="20368"></A>
Having thus removed everybody's right to create objects, we can selectively loosen the restriction. If, for example, we want to create a class for printers, but we also want to abide by the constraint that there is only one printer available to us, we can encapsulate the printer object inside a function so that everybody has access to the printer, but only a single printer object is <NOBR>created:<SCRIPT>create_link(32);</SCRIPT>
</NOBR></P>
<A NAME="20406"></A>
<UL><PRE>
class PrintJob; // forward declaration
// see <A HREF="../EC/EC5_FR.HTM#6793" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 34'; return true" onMouseOut = "self.status = self.defaultStatus">Item E34</A>
<A NAME="71865"></A>
class Printer {
public:
void submitJob(const PrintJob& job);
void reset();
void performSelfTest();
<A NAME="7576"></A>
...
<A NAME="75556"></A>
friend Printer& thePrinter();
<A NAME="71872"></A>
<A NAME="p131"></A>private:
Printer();
Printer(const Printer& rhs);
<A NAME="7577"></A>
...
<A NAME="75555"></A>
};
<A NAME="20417"></A>
Printer& thePrinter()
{
static Printer p; // the single printer object
return p;
}
</PRE>
</UL>
<P><A NAME="dingp33"></A><A NAME="20437"></A>
There are three separate components to this design. First, the constructors of the <CODE>Printer</CODE> class are private. That suppresses object creation. Second, the global function <CODE>thePrinter</CODE> is declared a friend of the class. That lets <CODE>thePrinter</CODE> escape the restriction imposed by the private constructors. Finally, <CODE>thePrinter</CODE> contains a <i>static</i> <CODE>Printer</CODE> object. That means only a single object will be <NOBR>created.<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P> <P><A NAME="dingp34"></A><A NAME="20466"></A>
Client code refers to <CODE>thePrinter</CODE> whenever it wishes to interact with the system's lone printer. By returning a reference to a <CODE>Printer</CODE> object, <CODE>thePrinter</CODE> can be used in any context where a <CODE>Printer</CODE> object itself could <NOBR>be:<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>
<A NAME="20487"></A>
<UL><PRE>class PrintJob {
public:
PrintJob(const string& whatToPrint);
...
<A NAME="20492"></A>
};
<A NAME="20493"></A>
string buffer;
<A NAME="20497"></A>
... // put stuff in buffer
<A NAME="37714"></A>
thePrinter().reset();
thePrinter().submitJob(buffer);
</PRE>
</UL>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -