📄 counting.htm
字号:
<P><A NAME="dingp40"></A><A NAME="AUTO00018"></A>Yes, this is counterintuitive, and don't be surprised if your compilers don't yet support this rule, but the behavior I've described is correct. Let us therefore consider a different way of preventing the deletion of derived class objects via <CODE>Counter*</CODE> pointers. Let us consider making <CODE>Counter</CODE>'s destructor <CODE>protected</CODE>:<SCRIPT>create_link(40);</SCRIPT>
</P>
<A NAME="AUTO00061"></A><UL><PRE>
template<typename T>
class Counter {
...
protected:
~Counter();
...
...
};
class Widget: public Counter<Widget> { ... };
Counter<Widget> *pw = new Widget;
...
delete pw; // error! Can't call protected
// destructor
</PRE>
</UL>
<P><A NAME="dingp41"></A>This offers the behavior we want. We can still create <CODE>Widget</CODE> objects, and we can still destroy them, as long as we don't do it via <CODE>Counter*</CODE> pointers. When we destroy a static <CODE>Widget</CODE> or a stack-based <CODE>Widget</CODE>, the implicit call to the <CODE>Counter</CODE> destructor is attributed to the <CODE>Widget</CODE> destructor, and since the <CODE>Widget</CODE> destructor is a member function of a derived class, it has access to <CODE>Counter</CODE>'s protected members. Similarly, when we delete a <CODE>Widget</CODE> through a <CODE>Widget*</CODE> pointer, the implicit call to the <CODE>Counter</CODE> destructor is attributed to the <CODE>Widget</CODE> <NOBR>destructor.<SCRIPT>create_link(41);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp42"></A><A NAME="AUTO00019"></A>Unfortunately, this design smells a bit of Bait and Switch. By having <CODE>Widget</CODE> publicly inherit from <CODE>Counter</CODE>, we're saying that every <CODE>Widget</CODE> object <EM>isa</EM> <CODE>Counter</CODE> object (<SCRIPT>sendmetoo(35,6914,'E');</SCRIPT> onMouseOver = "self.status = 'Item E35'; return true" onMouseOut = "self.status = self.defaultStatus">Item E35</A>). As a result, <CODE>Widget*</CODE> pointers are implicitly and automatically converted to <CODE>Counter*</CODE> pointers whenever it's necessary. <CODE>Widget</CODE> clients can thus be forgiven for expecting that they can delete <CODE>Widget</CODE>s via base class pointers; it's just one of the things public inheritance implies. Yet the above design disallows it. We get the behavior we want, yes, and that's worth a lot, but it just doesn't <em>feel</em> right. (The earlier design — the one involving a private <CODE>operator</CODE> <CODE>delete</CODE> — exhibits the same undesireable <NOBR>behavior.)<SCRIPT>create_link(42);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp43"></A><A NAME="AUTO00020"></A>Preventing deletion of derived class objects via public base class pointers looks unsatisfactory no matter how we slice it. I say we abandon this approach and turn our attention to using a <CODE>Counter</CODE> data member <NOBR>instead.<SCRIPT>create_link(43);</SCRIPT>
</NOBR></p>
<P><A NAME="dingp44"></A><FONT ID="aititle">Using a Data Member</FONT><SCRIPT>create_link(44);</SCRIPT>
</P>
<P><A NAME="dingp45"></A>We've already seen that the design based on a <CODE>Counter</CODE> data member has one drawback: clients must both define a <CODE>Counter</CODE> data member and write an inline version of <CODE>howMany</CODE> that calls the <CODE>Counter</CODE>'s <CODE>howMany</CODE> function. That's marginally more work than we'd like to impose on clients, but it's hardly unmanageable. There is another drawback, however. The addition of a <CODE>Counter</CODE> data member to a class will often increase the size of objects of that class <NOBR>type.<SCRIPT>create_link(45);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp46"></A><A NAME="AUTO00021"></A>At first blush, this is hardly a revelation. After all, how surprising is it that adding a data member to a class makes objects of that type bigger? But blush again. Look at the definition of <CODE>Counter</CODE>:<SCRIPT>create_link(46);</SCRIPT>
</P>
<A NAME="AUTO00062"></A><UL><PRE>
template<typename T>
class Counter {
public:
Counter();
Counter(const Counter&);
~Counter();
static size_t howMany();
private:
static size_t count;
};
</PRE>
</UL>
<P><A NAME="dingp47"></A>Notice how it has no nonstatic data members. That means each object of type <CODE>Counter</CODE> contains <I>nothing</I>. Might we hope that objects of type <CODE>Counter</CODE> have size zero? We might, but it would do us no good. C++ is quite clear on this point. All objects have a size of at least one byte, even objects with no nonstatic data members. By definition, <CODE>sizeof</CODE> will yield some positive number for each class instantiated from the <CODE>Counter</CODE> template. So each client class containing a <CODE>Counter</CODE> object will contain more data than it would if it didn't contain the <CODE>Counter</CODE>.<SCRIPT>create_link(47);</SCRIPT>
</P>
<P><A NAME="dingp48"></A><A NAME="AUTO00022"></A>(Interestingly, this does not imply that the size of a class without a <CODE>Counter</CODE> will necessarily be bigger than the size of the same class containing a <CODE>Counter</CODE>. That's because alignment restrictions can enter into the matter. For example, if <CODE>Widget</CODE> is a class containing two bytes of data but that's required to be four-byte aligned, each object of type <CODE>Widget</CODE> will contain two bytes of padding, and <CODE>sizeof(Widget)</CODE> will return 4. If, as is common, compilers satisfy the requirement that no objects have zero size by inserting a <CODE>char</CODE> into <CODE>Counter<Widget></CODE>, it's likely that <CODE>sizeof(Widget)</CODE> will still yield 4 even if <CODE>Widget</CODE> contains a <CODE>Counter<Widget></CODE> object. That object will simply take the place of one of the bytes of padding that <CODE>Widget</CODE> already contained. This is not a terribly common scenario, however, and we certainly can't plan on it when designing a way to package object-counting <NOBR>capabilities.)<SCRIPT>create_link(48);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp49"></A><A NAME="AUTO00023"></A>
I'm writing this at the very beginning of the Christmas season. (It is in fact Thanksgiving Day, 1997, which gives you some idea of how I celebrate major holidays...) Already I'm in a Bah Humbug mood. All I want to do is count objects, and I don't want to haul along any extra baggage to do it. There has <I>got</I> to be a <NOBR>way.<SCRIPT>create_link(49);</SCRIPT>
</NOBR></p>
<P><A NAME="dingp50"></A><FONT ID="aititle">Using Private Inheritance</FONT><SCRIPT>create_link(50);</SCRIPT>
</P>
<P><A NAME="dingp51"></A>Look again at the inheritance-based code that led to the need to consider a virtual destructor in <CODE>Counter</CODE>:<SCRIPT>create_link(51);</SCRIPT>
</P>
<A NAME="AUTO00063"></A>
<UL><PRE>class Widget: public Counter<Widget>
{ ... };
Counter<Widget> *pw = new Widget;
...
delete pw; // yields undefined results if
// Counter lacks a virtual destructor</PRE>
</UL>
<P><A NAME="dingp52"></A>Earlier we tried to prevent this sequence of operations by preventing the <CODE>delete</CODE> expression from compiling, but we discovered that that led to invalid or misleading code. There is something else we can prohibit. We can prohibit the implicit conversion from a <CODE>Widget*</CODE> pointer (which is what <CODE>new</CODE> returns) to a <CODE>Counter<Widget>*</CODE> pointer. In other words, we can prevent inheritance-based pointer conversions. All we have to do is replace the use of public inheritance with private <NOBR>inheritance:<SCRIPT>create_link(52);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00064"></A><UL><PRE>
class Widget: // inheritance is
private Counter<Widget> { ... }; // now private
Counter<Widget> *pw = // error! no implicit conversion
new Widget; // from Widget* to Counter<Widget>*
</PRE>
</UL>
<P><A NAME="dingp53"></A>Furthermore, we're likely to find that the use of <CODE>Counter</CODE> as a base class does not increase the size of <CODE>Widget</CODE> compared to <CODE>Widget</CODE>'s stand-alone size. Yes, I know, I just finished telling you that no class has zero size, but — well, that's not really what I said. What I said was that no <I>objects</I> have zero size. The C++ Standard makes clear that the base-class part of a more derived object <em>may</em> have zero size. In fact many compilers implement what has come to be known as the <I>empty base optimization</I> [<a href="#ref2" onMouseOver = "self.status = 'Reference 2'; return true" onMouseOut = "self.status = self.defaultStatus">Reference 2</A></NOBR>]. Thus, if a <CODE>Widget</CODE> <I>contains</I> a <CODE>Counter</CODE>, the size of the <CODE>Widget</CODE> must increase. The <CODE>Counter</CODE> data member is an object in its own right, hence it must have nonzero size. But if <CODE>Widget</CODE> <I>inherits</I> from <CODE>Counter</CODE>, compilers are allowed to keep <CODE>Widget</CODE> the same size it was before. This suggests an interesting rule of thumb for designs where space is tight and empty classes are involved: prefer private inheritance to containment when both will <NOBR>do.<SCRIPT>create_link(53);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp54"></A><A NAME="AUTO00024"></A>This last design is nearly perfect. It fulfills the efficiency criterion, provided your compilers implement the empty base optimization, because inheriting from <CODE>Counter</CODE> adds no per-object data to the inheriting class, and all <CODE>Counter</CODE> member functions are inline. It fulfills the foolproof criterion, because count manipulations are handled automatically by <CODE>Counter</CODE> member functions, those functions are automatically called by C++, and the use of private inheritance prevents implicit conversions that would allow derived-class objects to be manipulated as if they were base-class objects. (Okay, it's not totally foolproof: <CODE>Widget</CODE>'s author might foolishly instantiate <CODE>Counter</CODE> with a type other than <CODE>Widget</CODE>, i.e., <CODE>Widget</CODE> could be made to inherit from <CODE>Counter<Gidget></CODE>. I choose to ignore this <NOBR>possibility.)<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp55"></A><A NAME="AUTO00025"></A>The design is certainly easy for clients to use, but some may grumble that it could be easier. The use of private inheritance means that <CODE>howMany</CODE> will become private in inheriting classes, so such classes must include a <CODE>using</CODE> declaration to make <CODE>howMany</CODE> public to their <NOBR>clients:<SCRIPT>create_link(55);</SCRIPT>
</NOBR></P>
<!-- when adding special characters in <A NAME="AUTO00065"></A><UL><PRE> tags (like >) make sure to add extra spaces to
compensate -->
<A NAME="AUTO00026"></A>
<A NAME="AUTO00066"></A><UL><PRE>
class Widget: private Counter<Widget> {
public:
using Counter<Widget>::howMany; // make howMany public
... // rest of Widget
}; // is unchanged
class ABCD: private Counter<ABCD> {
public:
using Counter<ABCD>::howMany; // make howMany public
... // rest of Widget
}; // is unchanged
</PRE>
</UL>
<P><A NAME="dingp56"></A>For compilers not supporting name spaces, the same thing is accomplished by replacing the <CODE>using</CODE> declaration with the older (now deprecated) access <NOBR>declaration:<SCRIPT>create_link(56);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00067"></A><UL><PRE>
class Widget: private Counter<Widget> {
public:
Counter<Widget>::howMany; // make howMany public
... // rest of Widget
}; // is unchanged
class ABCD: private Counter<ABCD> {
public:
Counter<ABCD>::howMany; // make howMany public
... // rest of ABCD
}; // is unchanged
</PRE>
</UL>
<P><A NAME="dingp57"></A>Hence, clients who want to count objects and who want to make that count available (as part of their class's interface) to their clients must do two things: declare <CODE>Counter</CODE> as a base class and make <CODE>howMany</CODE> accessible. (Simple variations on this design make it possible for <CODE>Widget</CODE> to use <CODE>Counter<Widget></CODE> to count objects without making the count available to <CODE>Widget</CODE> clients, not even by calling <CODE>Counter<Widget>::howMany</CODE> directly. It is an interesting exercise to come up with one or more such <NOBR>variations.)<SCRIPT>create_link(57);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp58"></A><A NAME="AUTO00027"></A>The use of inheritance does, however, lead to two conditions that are worth noting. The first is ambiguity. Suppose we want to count <CODE>Widget</CODE>s, and we want to make the count available for general use. As shown above, we have <CODE>Widget</CODE> inherit from <CODE>Counter<Widget></CODE> and we make <CODE>howMany</CODE> public in <CODE>Widget</CODE>. Now suppose we have a class <CODE>SpecialWidget</CODE> publicly inherit from <CODE>Widget</CODE> and we want to offer <CODE>SpecialWidget</CODE> clients the same functionality <CODE>Widget</CODE> clients enjoy. No problem, we just have <CODE>SpecialWidget</CODE> inherit from <CODE>Counter<SpecialWidget></CODE>.<SCRIPT>create_link(58);</SCRIPT>
</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -