📄 sutter.htm
字号:
<UL><PRE>T& Top() {
if( vused_ == 0) {
throw "empty stack";
}
return v_[vused_-1];
}</PRE>
</UL>
<P><A NAME="dingp100"></A>The <CODE>Top</CODE> function hasn't changed at <NOBR>all.<SCRIPT>create_link(100);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00114"></A>
<UL><PRE>void Pop() {
if( vused_ == 0) {
throw "pop from empty stack";
} else {
--vused_;
destroy( v_+vused_ ); // see <A HREF="#side1" ONMOUSEOVER = "self.status = 'Sidebar 1'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Sidebar 1</A> for info
} // on destroy
}
}</PRE>
</UL>
<P><A NAME="dingp101"></A>Neither has <CODE>Pop</CODE>, save the new call to <NOBR>destroy.<SCRIPT>create_link(101);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp102"></A><A NAME="AUTO00050"></A>In summary, <CODE>Push</CODE> has been simplified, but the biggest benefit of encapsulating the resource ownership in a separate class was seen in <CODE>Stack</CODE>'s constructor and destructor. Thanks to <CODE>StackImpl</CODE>, we can go on to write as many more constructors as we like without having to worry about cleanup code, whereas last time each constructor would have had to know about the cleanup <NOBR>itself.<SCRIPT>create_link(102);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp103"></A><A NAME="AUTO00051"></A>You may also have noticed that even the lone <CODE>try/catch</CODE> we had to include in the first version of this class has now been eliminated — that is, we've written a fully exception-safe and exception-neutral generic container without writing a single <CODE>try</CODE>! (Who says writing exception-safe code is <NOBR>trying?)<SCRIPT>create_link(103);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp104"></A><FONT ID="aititle">Technique 2: Private Member</FONT><SCRIPT>create_link(104);</SCRIPT>
</P>
<P><A NAME="dingp105"></A>Next, consider what happens if <CODE>StackImpl</CODE>'s missing <CODE>/*????*/</CODE> access specifier is <CODE>public</CODE>.<SCRIPT>create_link(105);</SCRIPT>
</P>
<P><A NAME="dingp106"></A><A NAME="AUTO00052"></A>Using <CODE>public</CODE> hints that <CODE>StackImpl</CODE> is intended to be used as a <code>struct</code> by some external client, because its data members are public. So again <CODE>Stack</CODE> will be "implemented in terms of" <CODE>StackImpl</CODE>, only this time using a HAS-A containment relationship (see <SCRIPT>sendmetoo(40,7424,'E');</SCRIPT> ONMOUSEOVER = "self.status = 'Item E40'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item E40</A>) instead of private inheritance. We still have the same clear division of responsibilities: the <CODE>StackImpl</CODE> object will take care of managing the memory buffer and destroying all <CODE>T</CODE> objects remaining during <CODE>Stack</CODE> destruction, and the containing <CODE>Stack</CODE> will take care of constructing <CODE>T</CODE> objects within the raw memory. Because subobjects are created before a class's constructor body is entered, the raw memory management still takes place pretty much entirely outside <CODE>Stack</CODE>, because, for example, the initial allocation must fully succeed before any <CODE>Stack</CODE> constructor body can be <NOBR>entered.<SCRIPT>create_link(106);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp107"></A><A NAME="AUTO00053"></A>This implementation of <CODE>Stack</CODE> is only slightly different from the above. For example, <CODE>Count</CODE> returns <CODE>impl_.vused_</CODE> instead of just an inherited <CODE>vused_</CODE>. See <A HREF="#list1" ONMOUSEOVER = "self.status = 'Listing 1'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Listing 1</A> for the complete <NOBR>code.<SCRIPT>create_link(107);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp108"></A><FONT ID="aititle">Which Technique Is Better?</FONT><SCRIPT>create_link(108);</SCRIPT>
</P>
<P><A NAME="dingp109"></A>So, how do you choose between using <CODE>StackImpl</CODE> as a private base class or as a member object? After all, both give essentially the same effect and nicely separate the two concerns of memory management and object <NOBR>construction/destruction.<SCRIPT>create_link(109);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp110"></A><A NAME="AUTO00054"></A>When deciding between private inheritance and containment, my rule of thumb is to always prefer the latter and use inheritance only when absolutely necessary. Both techniques mean "is implemented in terms of," and containment forces a better separation of concerns because the using class is a normal client with access to only the used class' public interface. Use private inheritance instead of containment only when absolutely necessary, which means when <NOBR>either:<SCRIPT>create_link(110);</SCRIPT>
</NOBR></P>
<UL>
<A NAME="dingp111"></A><LI> you need access to the class's protected members;<SCRIPT>create_link(111);</SCRIPT>
<A NAME="AUTO00055"></A>
<A NAME="dingp112"></A><LI> you need to override a virtual function; or<SCRIPT>create_link(112);</SCRIPT>
<A NAME="AUTO00056"></A>
<A NAME="dingp113"></A><LI> the object needs to be constructed before other base subobjects.<SCRIPT>create_link(113);</SCRIPT>
</UL>
<P><A NAME="dingp114"></A>Admittedly, in this case it's tempting to use private inheritance anyway for syntactic convenience so that we wouldn't have to write "<CODE>impl_</CODE>." in so many <NOBR>places.<SCRIPT>create_link(114);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp115"></A><A NAME="AUTO00057"></A>(In the special case of an empty class, private inheritance may allow for the generation of more compact objects. See Scott Meyers' article, <A HREF="CO_FRAME.HTM" TARGET="_top" ONMOUSEOVER = "self.status = 'Counting Objects in C++'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" TARGET="_top">Counting Objects in C++</A>, for <NOBR>details.)<SCRIPT>create_link(115);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp116"></A><FONT ID="aititle">Relaxing the Requirements on <CODE>T</CODE></FONT><SCRIPT>create_link(116);</SCRIPT>
</P>
<P><A NAME="dingp117"></A>When writing a templated class, particularly something as potentially widely useful as a generic container, always ask yourself one crucial question: How reusable is my class? That is, what constraints have I put upon users of the class, and do those constraints unduly limit what those users might want to reasonably do with my <NOBR>class?<SCRIPT>create_link(117);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp118"></A><A NAME="AUTO00058"></A>These <CODE>Stack</CODE> templates have two major differences from the one we originally considered. We've discussed one already: They decouple memory management from contained object construction and destruction, which is nice but doesn't really affect users. However, there is another important difference: the new <CODE>Stacks</CODE> construct and destroy individual objects in-place as needed, instead of creating default <CODE>T</CODE> objects in the entire buffer and then assigning them as <NOBR>needed.<SCRIPT>create_link(118);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp119"></A><A NAME="AUTO00059"></A>This second difference turns out to have significant benefits: Better efficiency, and reduced requirements on <CODE>T</CODE>, the contained type. Our original <CODE>Stack</CODE>s from last time required <CODE>T</CODE> to provide four <NOBR>operations:<SCRIPT>create_link(119);</SCRIPT>
</NOBR></P>
<UL>
<A NAME="dingp120"></A><LI>default constructor (to construct the <CODE>v_</CODE> buffers)<SCRIPT>create_link(120);</SCRIPT>
<A NAME="AUTO00060"></A>
<A NAME="dingp121"></A><LI>copy constructor (if <CODE>Pop</CODE> returns by value)<SCRIPT>create_link(121);</SCRIPT>
<A NAME="AUTO00061"></A>
<A NAME="dingp122"></A><LI>nonthrowing destructor (to be able to guarantee exception safety)<SCRIPT>create_link(122);</SCRIPT>
<A NAME="AUTO00062"></A>
<A NAME="dingp123"></A><LI><EM>exception-safe</EM> copy assignment (to set the values in <CODE>v_</CODE>, and if the copy assignment throws then it must guarantee that the target object is unchanged; note that this is the only <CODE>T</CODE> member function which must be exception-safe in order for our <CODE>Stack</CODE> to be exception-safe)<SCRIPT>create_link(123);</SCRIPT>
</UL>
<P><A NAME="dingp124"></A><A NAME="AUTO00063"></A>Now, however, no default construction is needed because the only <CODE>T</CODE> construction that's ever performed is copy construction. Further, no copy assignment is needed because <CODE>T</CODE> objects are never assigned within <CODE>Stack</CODE> or <CODE>StackImpl</CODE>. On the other hand, we now always need a copy constructor. This means that the new <CODE>Stacks</CODE> require only two things of <CODE>T</CODE>:<SCRIPT>create_link(124);</SCRIPT>
</P>
<UL>
<A NAME="dingp125"></A><LI> copy constructor<SCRIPT>create_link(125);</SCRIPT>
<A NAME="AUTO00064"></A>
<A NAME="dingp126"></A><LI> nonthrowing destructor (to be able to guarantee exception safety)<SCRIPT>create_link(126);</SCRIPT>
</UL>
<P><A NAME="dingp127"></A><A NAME="AUTO00065"></A>How does this measure up to our original question about usability? Well, while it's true that many classes have both default constructors and copy assignment operators, many useful classes do not (see <SCRIPT>sendmetoo(4,5218,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'Item M4'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item M4</A>). (In fact, some objects simply cannot be assigned to, such as objects that contain reference members because these cannot be reseated.) Now even these can be put into <CODE>Stacks</CODE>, whereas in the original version they could not. That's definitely a big advantage over the original version, and one that quite a few users are likely to appreciate as <CODE>Stack</CODE> gets reused over <NOBR>time.<SCRIPT>create_link(127);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp128"></A><FONT ID="aititle">Should <CODE>Stack</CODE> Provide Exception Specifications?</FONT><SCRIPT>create_link(128);</SCRIPT>
</P>
<P><A NAME="dingp129"></A>In short: No, because we the authors of <CODE>Stack</CODE> don't know enough, and we still probably wouldn't want to even if we did know enough. The same is true in principle for any generic <NOBR>container.<SCRIPT>create_link(129);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp130"></A><A NAME="AUTO00066"></A>First, consider what we as the authors of <CODE>Stack</CODE> do know about <CODE>T</CODE>, the contained type: The answer is, precious little. In particular, we don't know in advance which <CODE>T</CODE> operations might throw or what they might throw. We could always get a little fascist about it and start dictating additional requirements on <CODE>T</CODE>, which would certainly let us know more about <CODE>T</CODE> and maybe add some useful exception specifications to <CODE>Stack</CODE>'s member functions. However, doing that would run completely counter to the goal of making <CODE>Stack</CODE> widely reusable, and so it's really out of the <NOBR>question.<SCRIPT>create_link(130);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp131"></A><A NAME="AUTO00067"></A>Next, you might notice that some container operations (e.g., <CODE>Count</CODE>) simply return a scalar value and are known not to throw. Isn't it possible to declare these as <CODE>throw()</CODE>? Yes, but there are two good reasons why you probably <NOBR>wouldn't:<SCRIPT>create_link(131);</SCRIPT>
</NOBR></P>
<OL>
<A NAME="dingp132"></A><LI> Writing <CODE>throw()</CODE> limits you in the future in case you want to change the underlying implementation to a form which could throw. (See <SCRIPT>sendmetoo(32,5373,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'Item M32'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item M32</A> for a discussion of worrying about what may happen in the future.) Loosening an exception specification always runs some risk of breaking existing clients (because the new revision of the class breaks an old promise), and so your class will be inherently more resistant to change and therefore more brittle. (Writing <CODE>throw()</CODE> on virtual functions can also make classes less extensible, because it greatly restricts people who might want to derive from your classes. It can make sense, but such a decision requires careful thought.)<SCRIPT>create_link(132);</SCRIPT>
<A NAME="AUTO00068"></A>
<A NAME="dingp133"></A><LI> Exception specifications can incur a performance overhead whether an exception is thrown or not, although many compilers are getting better at minimizing this. For widely-used operations and general-purpose containers, it may be better not to use exception specifications in order to avoid this overhead (see <SCRIPT>sendmetoo(15,40989,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'Item M15'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item M15</A>).<SCRIPT>create_link(133);</SCRIPT>
</OL>
<A NAME="AUTO00069"></A>
<P><A NAME="dingp134"></A><FONT ID="aititle"><A NAME="destruct"></A>Destructors That Throw and Why They're Evil</FONT><SCRIPT>create_link(134);</SCRIPT>
</P>
<P><A NAME="dingp135"></A>This brings us to our last topic, namely the innocent-looking <nobr><CODE>delete[]</CODE></nobr> <CODE>p;</CODE>. What does it really do? And how safe is <NOBR>it?<SCRIPT>create_link(135);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp136"></A><A NAME="AUTO00070"></A>First, recall our standard <CODE>destroy</CODE> helper function (see <A HREF="#side1" ONMOUSEOVER = "self.status = 'Sidebar 1'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Sidebar 1</A>):<SCRIPT>create_link(136);</SCRIPT>
</P>
<A NAME="AUTO00115"></A>
<UL><PRE>template <class FwdIter>
void destroy( FwdIter first, FwdIter last ) {
while( first != last ) {
destroy( first ); // calls "*first"'s dtor
++first;
}
}</PRE>
</UL>
<P><A NAME="dingp137"></A>This was safe in our example above because we required that <CODE>T</CODE> destructors never throw. But what if a contained object's destructor were allowed to throw? Well, consider what happens if <CODE>destroy</CODE> is passed a range of five objects: If the first destructor throws, then as it is written now destroy will exit and the other four objects will never be destroyed! This is obviously not a good <NOBR>thing.<SCRIPT>create_link(137);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp138"></A><A NAME="AUTO00071"></A>"Ah," you might interrupt, "but can't we clearly get around that by writing <CODE>destroy</CODE> to work properly in the face of <CODE>T</CODE>'s whose destructors are allowed to throw?" Well, that's not as clear as you might think. For exampl
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -