📄 counting.htm
字号:
Counter c;
};</PRE>
</UL>
<P><A NAME="dingp21"></A>The other way is to declare <CODE>Counter</CODE> as a base class, like <NOBR>this:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00053"></A>
<UL><PRE>// inherit from Counter to count objects
class Widget: public Counter {
... // all the usual public Widget stuff
private:
... // all the usual private Widget stuff
};</PRE>
</UL>
<P><A NAME="dingp22"></A>Both approaches have advantages and disadvantages. But before we examine them, we need to observe that neither approach will work in its current form. The problem has to do with the static object <CODE>count</CODE> inside <CODE>Counter</CODE>. There's only one such object, but we need one for <I>each class</I> using <CODE>Counter</CODE>. For example, if we want to count both <CODE>Widget</CODE>s and <CODE>ABCD</CODE>s, we need two static <CODE>size_t</CODE> objects, not one. Making <CODE>Counter::count</CODE> nonstatic doesn't solve the problem, because we need one counter per class, not one counter per <NOBR>object.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp23"></A><A NAME="AUTO00010"></A>We can get the behavior we want by employing one of the best-known but oddest-named tricks in all of C++: we turn <CODE>Counter</CODE> into a template, and each class using <CODE>Counter</CODE> instantiates the template with itself as the template <NOBR>argument.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp24"></A><A NAME="AUTO00011"></A>Let me say that again. First, <CODE>Counter</CODE> becomes a <NOBR>template:<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00054"></A>
<UL><PRE>template<typename T>
class Counter {
public:
Counter() { ++count; }
Counter(const Counter&) { ++count; }
~Counter() { --count; }
static size_t howMany()
{ return count; }
private:
static size_t count;
};
template<typename T>
size_t Counter<T>::count = 0; // this may now go in a header file</PRE>
</UL>
<P><A NAME="dingp25"></A>The first <CODE>Widget</CODE> implementation choice then looks like <NOBR>this:<SCRIPT>create_link(25);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00055"></A>
<UL><PRE>class Widget { // embed a Counter to count objects
public:
...
static size_t howMany()
{return Counter<Widget>::howMany();}
private:
...
Counter<Widget> c;
};
</PRE>
</UL>
<P><A NAME="dingp26"></A>And the second choice now looks <NOBR>like:<SCRIPT>create_link(26);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00056"></A><UL><PRE>
class Widget: public Counter<Widget> { // inherit from Counter
public: // to count objects
...
};
</PRE>
</UL>
<P><A NAME="dingp27"></A>Notice how in both cases we replace <CODE>Counter</CODE> with <CODE>Counter<Widget></CODE>. As I said earlier, each class using <CODE>Counter</CODE> instantiates the template with itself as the <NOBR>argument.<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp28"></A><A NAME="AUTO00012"></A>The tactic of a class instantiating a template for its own use by passing itself as the template argument was first publicized by <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>°</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=jcoplien" onMouseOver = "self.status = 'Jim Coplien Personal Site'; return true" onMouseOut = "self.status = self.defaultStatus" target="_top">Jim</NOBR> Coplien</A>. He showed that it's used in many languages (not just C++) and he called it "a curiously recurring template pattern" [<a href="#ref1" onMouseOver = "self.status = 'Reference 1'; return true" onMouseOut = "self.status = self.defaultStatus">Reference 1</A>]. I don't think Jim intended it, but his description of the pattern has pretty much become its name. That's too bad, because pattern names are important, and this one fails to convey information about what it does or how it's <NOBR>used.<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp29"></A><A NAME="AUTO00013"></A>The naming of patterns is as much art as anything else, and I'm not very good at it, but I'd probably call this pattern something like "Do It For Me." Basically, each class generated from <CODE>Counter</CODE> provides a service (it counts how many objects exist) for the class requesting the <CODE>Counter</CODE> instantiation. So the class <CODE>Counter<Widget></CODE> counts <CODE>Widget</CODE>s, and the class <CODE>Counter<ABCD></CODE> counts <CODE>ABCD</CODE>s.<SCRIPT>create_link(29);</SCRIPT>
</P>
<P><A NAME="dingp30"></A><A NAME="AUTO00014">Now that <CODE>Counter</CODE> is a template, both the embedding design and the inheritance design will work, so we're in a position to evaluate their comparative strengths and weaknesses. One of our design criteria was that object-counting functionality should be easy for clients to obtain, and the code above makes clear that the inheritance-based design is easier than the embedding-based design. That's because the former requires only the mentioning of <CODE>Counter</CODE> as a base class, whereas the latter requires that a <CODE>Counter</CODE> data member be defined <I>and</I> that <CODE>howMany</CODE> be re-implemented by clients to invoke <CODE>Counter</CODE>'s <CODE>howMany</CODE>. (An alternative is to omit <CODE>Widget::howMany</CODE> and make clients call <CODE>Counter<Widget>::howMany</CODE> directly. For the purposes of this article, however, we'll assume we want <CODE>howMany</CODE> to be part of the <CODE>Widget</CODE> interface.) That's not a lot of additional work (client <CODE>howMany</CODE>s are simple inline functions), but having to do one thing is easier than having to do two. So let's first turn our attention to the design employing <NOBR>inheritance.<SCRIPT>create_link(30);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp31"></A><FONT ID="aititle">Using Public Inheritance</FONT><SCRIPT>create_link(31);</SCRIPT>
</P>
<P><A NAME="dingp32"></A>The design based on inheritance works because C++ guarantees that each time a derived class object is constructed or destroyed, its base class part will be constructed first and destroyed last. Making <CODE>Counter</CODE> a base class thus ensures that a <CODE>Counter</CODE> constructor or destructor will be called each time a class inheriting from it has an object created or <NOBR>destroyed.<SCRIPT>create_link(32);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp33"></A><A NAME="AUTO00015"></A>Any time the subject of base classes comes up, however, so does the subject of virtual destructors. Should <CODE>Counter</CODE> have one? Well-established principles of object-oriented design for C++ dictate that it should. If it has no virtual destructor, deletion of a derived class object via a base class pointer yields undefined (and typically undesirable) results (<SCRIPT>sendmetoo(14,223029,'E');</SCRIPT> onMouseOver = "self.status = 'Item E14'; return true" onMouseOut = "self.status = self.defaultStatus">Item E14</A>):<SCRIPT>create_link(33);</SCRIPT>
</P>
<A NAME="AUTO00057"></A><UL><PRE>
class Widget: public Counter<Widget> { ... };
Counter<Widget> *pw = // get base class ptr to
new Widget; // derived class object
...
delete pw; // yields undefined results if Counter<Widget>
// lacks a virtual destructor
</PRE>
</UL>
<P><A NAME="dingp34"></A>Such behavior would violate our criterion that our object-counting design be essentially foolproof, because there's nothing unreasonable about the code above. That's a powerful argument for giving <CODE>Counter</CODE> a virtual <NOBR>destructor.<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp35"></A><A NAME="AUTO00016"></A>Another criterion, however, was maximal efficiency (imposition of no unnecessary speed or space penalty for counting objects), and now we're in trouble. We're in trouble because the presence of a virtual destructor (or any virtual function) in <CODE>Counter</CODE> means each object of type <CODE>Counter</CODE> (or a class derived from <CODE>Counter</CODE>) will contain a (hidden) virtual pointer, and this will increase the size of such objects if they don't already support virtual functions. (For details, see <SCRIPT>sendmetoo(24,41284,'M');</SCRIPT> onMouseOver = "self.status = 'Item M24'; return true" onMouseOut = "self.status = self.defaultStatus" >Item M24</A>.) That is, if <CODE>Widget</CODE> itself contains no virtual functions, objects of type <CODE>Widget</CODE> would increase in size if <CODE>Widget</CODE> started inheriting from <CODE>Counter<Widget></CODE>. We don't want <NOBR>that.<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp36"></A><A NAME="AUTO00017"></A>The only way to avoid it is to find a way to prevent clients from deleting derived class objects via base class pointers. It seems that a reasonable way to achieve this is to declare <CODE>operator</CODE> <CODE>delete</CODE> private in <CODE>Counter</CODE>:<SCRIPT>create_link(36);</SCRIPT>
</P>
<A NAME="AUTO00058"></A><UL><PRE>
template<typename T>
class Counter {
public:
...
private:
void operator delete(void*);
...
};
</PRE>
</UL>
<P><A NAME="dingp37"></A>Now the <CODE>delete</CODE> expression won't <NOBR>compile:<SCRIPT>create_link(37);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00059"></A><UL><PRE>
class Widget: public Counter<Widget> { ... };
Counter<Widget> *pw = new Widget;
...
delete pw; // error! Can't call private
// operator delete
</PRE>
</UL>
<P><A NAME="dingp38"></A>Unfortunately — and this is the really interesting part — the <CODE>new</CODE> expression shouldn't compile <NOBR>either!<SCRIPT>create_link(38);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00060"></A><UL><PRE>
Counter<Widget> *pw = // this should not compile because
new Widget; // operator delete is private!
</PRE>
</UL>
<P><A NAME="dingp39"></A>Remember from my earlier discussion of <CODE>new</CODE>, <CODE>delete</CODE>, and exceptions that C++'s runtime system is responsible for de-allocating memory allocated by <CODE>operator</CODE> <CODE>new</CODE> if the subsequent constructor invocation fails. Recall also that <CODE>operator</CODE> <CODE>delete</CODE> is the function called to perform the deallocation. But we've declared <CODE>operator</CODE> <CODE>delete</CODE> private in <CODE>Counter</CODE>, which makes it invalid to create objects on the heap via <CODE>new</CODE>!<SCRIPT>create_link(39);</SCRIPT>
</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -