⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sutter.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
<HTML LANG="en">
<HEAD>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>
<TITLE>Exception-Safe Generic Containers. By Herb Sutter</TITLE>

<SCRIPT LANGUAGE="JavaScript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript">var imagemax = 0; setCurrentMax(0);</SCRIPT>
<SCRIPT LANGUAGE="JavaScript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript" SRC="../JAVA/SENDMETO.JS"></SCRIPT>

<SCRIPT LANGUAGE="JavaScript">
var dingbase = "SU_DIR.HTM";
var dingtext = "Sutter, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>

<BODY BGCOLOR="#FFFFFF" ONLOAD="setResize()">
<!-- SectionName="Sutter's Exception-Safe Generic Containers" -->
<HR SIZE="1" NOSHADE>
<FONT COLOR="#663300" FACE="Helvetica" SIZE=-1>
<B>This is an updated version of an article that appeared in the
    September and November-December 1997 issues of the <I><FONT COLOR="#FF0000" SIZE="-2">&deg;</FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cppreport"
    ONMOUSEOVER="self.status='C++ Report Home Page'; return true" ONMOUSEOUT="self.status=self.defaultStatus" TARGET="_top">C++ Report</A></I>.</FONT></B>
<HR SIZE="1" NOSHADE>

<P><A NAME="dingp1"></A><FONT ID="agtitle">Exception-Safe Generic Containers</FONT><SCRIPT>create_link(1);</SCRIPT>
</P>

<P><A NAME="dingp2"></A><A NAME="AUTO00001"></A>
<FONT FACE="Arial" SIZE="+1">by
<FONT COLOR="#FF0000" SIZE="-2">&deg;</FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=hs" ONMOUSEOVER = "self.status = 'Herb Sutter Home Page'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" TARGET="_top">Herb Sutter</A></FONT><SCRIPT>create_link(2);</SCRIPT>
</P>

<P><A NAME="dingp3"></A><A NAME="AUTO00002"></A>Exception handling and generic programming are two of C++'s most powerful features. Both, however, require exceptional care, and writing an efficient reusable generic container is nearly as difficult as  writing exception-safe <NOBR>code.<SCRIPT>create_link(3);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp4"></A><A NAME="AUTO00003"></A>This article tackles both of these major features at once, by examining how to write exception-safe (works properly in the presence of exceptions) and exception-neutral (propagates all exceptions to the caller) generic containers. That's easy enough to say, but it's no mean feat. If you have any doubts on that score, see Tom Cargill's excellent article, <A HREF="CA_FRAME.HTM" ONMOUSEOVER = "self.status = 'Exception Handling: A False Sense of Security'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" TARGET="_top">Exception Handling: A False Sense of Security</A>.<SCRIPT>create_link(4);</SCRIPT>
</P>

<P><A NAME="dingp5"></A><A NAME="AUTO00004"></A>This article begins where Cargill's left off, namely by presenting an exception-neutral version of the <CODE>Stack</CODE> template he critiques. In the end, we'll significantly improve the <CODE>Stack</CODE> container by reducing the requirements on <CODE>T</CODE>, the contained type, and show advanced techniques for managing resources exception-safely. Along the way we'll find the answers to questions like the <NOBR>following:<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>

<UL>
<A NAME="dingp6"></A><LI>What are the different &quot;levels&quot; of exception safety?<SCRIPT>create_link(6);</SCRIPT>

<A NAME="AUTO00005"></A>
<A NAME="dingp7"></A><LI>Can or should generic containers be fully exception-neutral?<SCRIPT>create_link(7);</SCRIPT>

<A NAME="AUTO00006"></A>
<A NAME="dingp8"></A><LI>Are the standard library containers exception-safe or -neutral?<SCRIPT>create_link(8);</SCRIPT>

<A NAME="AUTO00007"></A>
<A NAME="dingp9"></A><LI>Does exception safety affect the design of your container's public interface?<SCRIPT>create_link(9);</SCRIPT>

<A NAME="AUTO00008"></A>
<A NAME="dingp10"></A><LI>Should generic containers use exception specifications?<SCRIPT>create_link(10);</SCRIPT>


</UL>

<P><A NAME="dingp11"></A><FONT ID="aititle">The <CODE>Stack<></CODE> Container</FONT><SCRIPT>create_link(11);</SCRIPT>
</P>

<P><A NAME="dingp12"></A>Here is the declaration of the <CODE>Stack</CODE> template, substantially the same as in Cargill's article. Our mission: to make <CODE>Stack</CODE> exception-neutral. That is, <CODE>Stack</CODE> objects should always be in a correct and consistent state regardless of any exceptions that might be thrown in the course of executing <CODE>Stack</CODE>'s member functions, and if any exceptions are thrown they should be propagated seamlessly through to the caller, who can deal with them as he pleases because he knows the context of <CODE>T</CODE> and we <NOBR>don't.<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00091"></A>

<UL><PRE>template &lt;class T&gt; class Stack {
public:
  Stack();
  ~Stack();
  Stack(const Stack&);
  Stack& operator=(const Stack&);
  size_t Size() const;
  void   Push(const T&);
  T      Pop();               // if empty, throws exception
private:
  T*     v_;                  // ptr to a memory area big
  size_t vsize_;              //  enough for 'vsize_' T's
  size_t vused_;              // # of T's actually in use
};</PRE>
</UL>

<P><A NAME="dingp13"></A>Before reading on, stop and think about this container and consider: What are the exception-safety issues? How can this class be made exception-neutral, so that any exceptions are propagated to the caller without causing integrity problems in a <CODE>Stack</CODE> <NOBR>object?<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp14"></A><FONT ID="aititle">Default Construction</FONT><SCRIPT>create_link(14);</SCRIPT>
</P>

<P><A NAME="dingp15"></A>Right away, we can see that <CODE>Stack</CODE> is going to have to manage dynamic memory resources. Clearly one key is going to be avoiding leaks even in the presence of exceptions thrown by <CODE>T</CODE> operations and standard memory allocations. For now, we'll manage these memory resources within each <CODE>Stack</CODE> member function. Later, we'll improve on this by using a private base class (see <SCRIPT>sendmetoo(42,21052,'E');</SCRIPT> ONMOUSEOVER = "self.status = 'Item E42'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" >Item E42</A></NOBR>) to encapsulate resource <NOBR>ownership.<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp16"></A><A NAME="AUTO00009"></A>First, consider one possible default <NOBR>constructor:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00092"></A>

<UL><PRE>template&lt;class T&gt;
Stack&lt;T&gt;::Stack()
  : v_(0),
  vsize_(10),
  vused_(0)           // nothing used yet
{
  v_ = new T[vsize_]; // initial allocation
}</PRE>
</UL>

<P><A NAME="dingp17"></A>Is this constructor exception-safe? To find out, consider what might throw. In short, the answer is: &quot;Any function.&quot;  So the first step is to analyze this code and determine what functions will actually be called, including both free functions and constructors, destructors, operators, and other member <NOBR>functions.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp18"></A><A NAME="AUTO00010"></A>This <CODE>Stack</CODE> constructor first sets <CODE>vsize_</CODE> to <CODE>10</CODE>, then attempts to allocate some initial memory using <CODE>new</CODE> <CODE>T[vsize_]</CODE>. The latter first tries to call <CODE>operator</CODE> <nobr><CODE>new[]</CODE></nobr> (either the default <CODE>operator</CODE> <CODE>new[]</CODE> or one provided by <CODE>T</CODE> &#151; see <SCRIPT>sendmetoo(8,33985,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'Item M8'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item M8</A>) to allocate the memory, then tries to call <CODE>T::T</CODE> a total of <CODE>vsize_</CODE> times. There are two operations that might fail: first, the memory allocation itself, in which case operator <CODE>new[]</CODE> will throw a <CODE>bad_alloc</CODE> exception; and second, <CODE>T</CODE>'s default constructor, which might throw anything  at all, and in which case any objects that were constructed are destroyed and the allocated memory is automatically guaranteed to be deallocated via <CODE>operator</CODE> <nobr><CODE>delete[]</CODE></nobr>.  (See <SCRIPT>sendmetoo(8,33985,'M');</SCRIPT> ONMOUSEOVER = "self.status = 'Item M8'; return true" ONMOUSEOUT = "self.status = self.defaultStatus">Item M8</A> and the <A HREF="CO_FRAME.HTM#sidebar" ONMOUSEOVER = "self.status = 'sidebar'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" TARGET="_top">sidebar</A> to Scott Meyers' article, <A HREF="CO_FRAME.HTM" ONMOUSEOVER = "self.status = 'Counting Objects in C++'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" TARGET="_top">Counting Objects in C++</A>.)<SCRIPT>create_link(18);</SCRIPT>
</P>

<P><A NAME="dingp19"></A><A NAME="AUTO00011"></A>Hence the above function is fully exception-safe, and we can move on to the next <NOBR>...<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp20"></A><A NAME="AUTO00012"></A>... what? Why is it exception-safe, you ask? All right, let's examine it in a little more <NOBR>detail:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<OL>
<A NAME="dingp21"></A><LI><EM>We're exception-neutral</EM>. We don't catch anything, so if the new throws then the exception is correctly propagated up to our caller as required.<SCRIPT>create_link(21);</SCRIPT>

<A NAME="AUTO00013"></A>
<A NAME="dingp22"></A><LI><EM>We don't leak</EM>. If the <CODE>operator</CODE> <CODE>new[]</CODE> allocation call exited by throwing a <CODE>bad_alloc</CODE> exception, then no memory was allocated to begin with so there can't be a leak. If one of the <CODE>T</CODE> constructors threw, then  any <CODE>T</CODE> objects that were fully constructed were properly destroyed and finally <CODE>operator</CODE> <CODE>delete[]</CODE> was automatically called to release the memory. That makes us leak-proof, as advertised.  (I'm ignoring for now the possibility that one of the <CODE>T</CODE> destructor calls might throw during the cleanup, which would call <CODE>terminate()</CODE> and simply kill the program altogether and leave events well out of your control anyway. See below for more information on <A HREF="#destruct" ONMOUSEOVER = "self.status = 'Destructors That Throw and Why They are Evil'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" >Destructors That Throw and Why They're Evil</A>.)<SCRIPT>create_link(22);</SCRIPT>

<A NAME="AUTO00014"></A>
<A NAME="dingp23"></A><LI><EM>We're in a consistent state whether any part of the <CODE>new</CODE> throws or not</EM>. Now, you might think that if the <CODE>new</CODE> throws, then <CODE>vsize_</CODE> has already been set to 10 when in fact nothing was successfully allocated. Isn't that inconsistent? Not really, because it's irrelevant. Remember, if the new throws we propagate  the exception out of our own constructor, right? And, by definition, &quot;exiting a constructor by means of an exception&quot; means our <CODE>Stack</CODE> proto-object never actually got to become a  completely constructed object at all, its lifetime never started, and hence its state is meaningless because the object never existed. It doesn't matter what the memory that briefly held <CODE>vsize_</CODE> was set to, any more than it matters what the memory was set to after we leave an object's destructor. All that's left is raw memory, smoke and ashes.<SCRIPT>create_link(23);</SCRIPT>


</OL>

<P><A NAME="dingp24"></A><A NAME="AUTO00015"></A>All right, I'll admit it... I put the <CODE>new</CODE> in the constructor body purely to open the door for that last #3 discussion. What I'd actually prefer to write <NOBR>is:<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="AUTO00093"></A>

<UL><PRE>template&lt;class T&gt;
Stack&lt;T&gt;::Stack()
  : v_(new T[10]),  // default allocation
    vsize_(10),
    vused_(0)       // nothing used yet
{ }</PRE>
</UL>

<P><A NAME="dingp25"></A>Both versions are practically equivalent. I prefer the latter because it follows the usual good practice of initializing members in initializer lists whenever possible (see <SCRIPT>sendmetoo(12,2071,'E');</SCRIPT> ONMOUSEOVER = "self.status = 'Item E12'; return true" ONMOUSEOUT = "self.status = self.defaultStatus" >Item E12</A>).<SCRIPT>create_link(25);</SCRIPT>
</P>

<P><A NAME="dingp26"></A><FONT ID="aititle">Destruction</FONT><SCRIPT>create_link(26);</SCRIPT>
</P>

<P><A NAME="dingp27"></A>The destructor looks a lot easier, once we make a (greatly) simplifying <NOBR>assumption:<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -