📄 ei42.htm
字号:
<CODE>GenericStack</CODE> class, so they must also be deallocated inside that
class. As a result, the implementation of the <CODE>Stack</CODE> class in <A
HREF="./EI41_FR.HTM#7611" target="_top">Item 41</A> suffices almost perfectly
for the <CODE>GenericStack</CODE> class. The only changes you need to make
involve substitutions of <CODE>void*</CODE> for <CODE>T</CODE>.<SCRIPT>create_link(12);</SCRIPT>
</P>
<A NAME="21428"></A>
<P><A NAME="dingp13"></A>
<A NAME="p192"></A>The <CODE>GenericStack</CODE> class by itself is of little utility — it's too easy to misuse. For example, a client could mistakenly push a pointer to a <CODE>Cat</CODE> object onto a stack meant to hold only pointers to <CODE>int</CODE>s, and compilers would merrily accept it. After all, a pointer's a pointer when it comes to <CODE>void*</CODE> <NOBR>parameters.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="21431"></A>
<P><A NAME="dingp14"></A>
To regain the type safety to which you have become accustomed, you create <I>interface classes</I> to <CODE>GenericStack</CODE>, like <NOBR>this:<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="21434"></A>
<UL><PRE>
class IntStack { // interface class for ints
public:
void push(int *intPtr) { s.push(intPtr); }
int * pop() { return static_cast<int*>(s.pop()); }
bool empty() const { return s.empty(); }
</PRE>
</UL><A NAME="21683"></A>
<UL><PRE>private:
GenericStack s; // implementation
};
</PRE>
</UL><A NAME="21437"></A>
<UL><PRE>
class CatStack { // interface class for cats
public:
void push(Cat *catPtr) { s.push(catPtr); }
Cat * pop() { return static_cast<Cat*>(s.pop()); }
bool empty() const { return s.empty(); }
</PRE>
</UL><A NAME="21690"></A>
<UL><PRE>private:
GenericStack s; // implementation
};
</PRE>
</UL><A NAME="21439"></A>
<P><A NAME="dingp15"></A>
As you can see, the <CODE>IntStack</CODE> and <CODE>CatStack</CODE> classes
serve only to enforce strong typing. Only <CODE>int</CODE> pointers can be
pushed onto an <CODE>IntStack</CODE> or popped from it, and only
<CODE>Cat</CODE> pointers can be pushed onto a <CODE>CatStack</CODE> or
popped from it. Both <CODE>IntStack</CODE> and <CODE>CatStack</CODE> are
implemented in terms of the class <CODE>GenericStack</CODE>, a relationship
that is expressed through layering (see <A HREF="./EI40_FR.HTM#7424"
target="_top">Item 40</A>), and <CODE>IntStack</CODE> and
<CODE>CatStack</CODE> will share the code for the functions in
<CODE>GenericStack</CODE> that actually implement their behavior.
Furthermore, the fact that all <CODE>IntStack</CODE> and
<CODE>CatStack</CODE> member functions are (implicitly) <CODE>inline</CODE>
means that the runtime cost of using these interface classes is zip, zero,
nada, <NOBR>nil.<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P> <A
NAME="21713"></A> <P><A NAME="dingp16"></A> But what if potential clients
don't realize that? What if they mistakenly believe that use of
<CODE>GenericStack</CODE> is more efficient, or what if they're just wild and
reckless and think only wimps need type-safety nets? What's to keep them from
bypassing <CODE>IntStack</CODE> and <CODE>CatStack</CODE> and going straight
to <CODE>GenericStack</CODE>, where they'll be free to make the kinds of type
errors C++ was specifically designed to <NOBR>prevent?<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="21721"></A>
<P><A NAME="dingp17"></A>
Nothing. Nothing prevents that. But maybe something <NOBR>should.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="21448"></A>
<P><A NAME="dingp18"></A>
<A NAME="p193"></A>I mentioned at the outset of this Item that an alternative way to assert an is-implemented-in-terms-of relationship between classes is through private inheritance. In this case, that technique offers an advantage over layering, because it allows you to express the idea that <CODE>GenericStack</CODE> is too unsafe for general use, that it should be used only to implement other classes. You say that by protecting <CODE>GenericStack</CODE>'s member <NOBR>functions:<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="21452"></A>
<UL><PRE>class GenericStack {
protected:
GenericStack();
~GenericStack();
</PRE>
</UL><A NAME="21455"></A>
<UL><PRE> void push(void *object);
void * pop();
</PRE>
</UL><A NAME="21703"></A>
<UL><PRE> bool empty() const;
</PRE>
</UL><A NAME="21699"></A>
<UL><PRE>private:
... // same as above
};
</PRE>
</UL><A NAME="22091"></A>
<UL><PRE>
GenericStack s; // error! constructor is
// protected
</PRE>
</UL><A NAME="22094"></A>
<UL><PRE>
</PRE>
</UL><A NAME="22095"></A>
<UL><PRE>class IntStack: private GenericStack {
public:
void push(int *intPtr) { GenericStack::push(intPtr); }
int * pop() { return static_cast<int*>(GenericStack::pop()); }
bool empty() const { return GenericStack::empty(); }
};
</PRE>
</UL><A NAME="21459"></A>
<UL><PRE>class CatStack: private GenericStack {
public:
void push(Cat *catPtr) { GenericStack::push(catPtr); }
Cat * pop() { return static_cast<Cat*>(GenericStack::pop()); }
bool empty() const { return GenericStack::empty(); }
};
</PRE>
</UL><A NAME="22096"></A>
<UL><PRE>
IntStack is; // fine
</PRE>
</UL><A NAME="22097"></A>
<UL><PRE>
CatStack cs; // also fine
</PRE>
</UL><A NAME="21460"></A>
<P><A NAME="dingp19"></A>
Like the layering approach, the implementation based on private inheritance avoids code duplication, because the type-safe interface classes consist of nothing but inline calls to the underlying <CODE>GenericStack</CODE> <NOBR>functions.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="21462"></A>
<P><A NAME="dingp20"></A>
Building type-safe interfaces on top of the <CODE>GenericStack</CODE> class is a pretty slick maneuver, but it's awfully unpleasant to have to type in all those interface classes by hand. Fortunately, you don't have to. You <A NAME="p194"></A>can use templates to generate them automatically. Here's a template to generate type-safe stack interfaces using private <NOBR>inheritance:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="21464"></A>
<UL><PRE>template<class T>
class Stack: private GenericStack {
public:
void push(T *objectPtr) { GenericStack::push(objectPtr); }
T * pop() { return static_cast<T*>(GenericStack::pop()); }
bool empty() const { return GenericStack::empty(); }
};
</PRE>
</UL><A NAME="194778"></A>
<P><A NAME="dingp21"></A>
This is amazing code, though you may not realize it right away. Because of the template, compilers will automatically generate as many interface classes as you need. Because those classes are type-safe, client type errors are detected during compilation. Because <CODE>GenericStack</CODE>'s member functions are protected and interface classes use it as a private base class, clients are unable to bypass the interface classes. Because each interface class member function is (implicitly) declared <CODE>inline</CODE>, no runtime cost is incurred by use of the type-safe classes; the generated code is exactly the same as if clients programmed with <CODE>GenericStack</CODE> directly (assuming compilers respect the <CODE>inline</CODE> request — see <A HREF="./EI33_FR.HTM#6729" TARGET="_top">Item 33</A>). And because <CODE>GenericStack</CODE> uses <CODE>void*</CODE> pointers, you pay for only one copy of the code for manipulating stacks, no matter how many different types of stack you use in your program. In short, this design gives you code that's both maximally efficient and maximally type safe. It's difficult to do better than <NOBR>that.<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="21802"></A>
<P><A NAME="dingp22"></A>
One of the precepts of this book is that C++'s features interact in remarkable ways. This example, I hope you'll agree, is pretty <NOBR>remarkable.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<A NAME="21814"></A>
<P><A NAME="dingp23"></A>
The insight to carry away from this example is that it could not have been achieved using layering. Only inheritance gives access to protected members, and only inheritance allows for virtual functions to be redefined. (For an example of how the existence of virtual functions can motivate the use of private inheritance, see <A HREF="./EI43_FR.HTM#7778" TARGET="_top">Item 43</A>.) Because virtual functions and protected members exist, private inheritance is sometimes the only practical way to express an is-implemented-in-terms-of relationship between classes. As a result, you shouldn't be afraid to use private inheritance when it's the most appropriate implementation technique at your disposal. At the same time, however, layering is the preferable technique in general, so you should employ it whenever you <NOBR>can.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI41_FR.HTM" TARGET="_top">Item 41: Differentiate between inheritance and templates.</A> <BR> Continue to <A HREF="./EI43_FR.HTM" TARGET="_top">Item 43: Use multiple inheritance judiciously. </A></FONT></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -