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

📄 mc3.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>
<HEAD>
<TITLE>More Effective C++ | Chapter 3: Exceptions</TITLE>
<LINK REL=STYLESHEET HREF=../INTRO/ECMEC.CSS>

<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/COOKIE.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">var imagemax = 3; setCurrentMax(3);</SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/IMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/NSIMGDOC.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript" SRC="../JAVA/DINGBATS.JS"></SCRIPT>
<SCRIPT LANGUAGE="Javascript">
var dingbase = "MC3_DIR.HTM";
var dingtext = "MEC++ Exceptions, P";
if (self == top) {
 top.location.replace(dingbase + this.location.hash);
}
</SCRIPT>

</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" ONLOAD="setResize()">

<!-- SectionName="Chapter Intro: Exceptions" -->

<A NAME="71622"></A><A NAME="71623"> </A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC2_FR.HTM#33985" TARGET="_top" onMouseOver = "self.status = 'Back to MEC++ Item 8'; return true" onMouseOut = "self.status = self.defaultStatus">Item 8: Understand the different meanings of new and delete</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MC3.HTM#5292" onMouseOver = "self.status = 'Item 9: Use destructors to prevent resource leaks.'; return true" onMouseOut = "self.status = self.defaultStatus">Item 9: Use destructors to prevent resource leaks.</A></FONT></DIV>
<A NAME="p44"></A><P><A NAME="dingp1"></A><font ID="mgtitle">Exceptions</font><SCRIPT>create_link(1);</SCRIPT>
</P>

<A NAME="71624"> </A>
<P><A NAME="dingp2"></A>
The addition of exceptions to C++ changes things. Profoundly. Radically. Possibly uncomfortably. The use of raw, unadorned pointers, for example, becomes risky. Opportunities for resource leaks increase in number. It becomes more difficult to write constructors and destructors that behave the way we want them to. Special care must be taken to prevent program execution from abruptly halting. Executables and libraries typically increase in size and decrease in <NOBR>speed.<SCRIPT>create_link(2);</SCRIPT>
</NOBR></P><A NAME="81472"> </A>
<P><A NAME="dingp3"></A>
And these are just the things we know. There is much the C++ community does not know about writing programs using exceptions, including, for the most part, how to do it correctly. There is as yet no agreement on a body of techniques that, when applied routinely, leads to software that behaves predictably and reliably when exceptions are thrown. (For insight into some of the issues involved, see <A HREF="../MAGAZINE/CA_FRAME.HTM" TARGET="_top" onMouseOver = "self.status = 'Link to the article by Tom Cargill'; return true" onMouseOut = "self.status = self.defaultStatus">the article by Tom Cargill</A>. For information on recent progress in dealing with these issues, see the articles by <A HREF="../MAGAZINE/RE_FRAME.HTM" TARGET="_top" onMouseOver = "self.status = 'Link to the articles by Jack Reeves'; return true" onMouseOut = "self.status = self.defaultStatus">Jack Reeves</A> and <A HREF="../MAGAZINE/SU_FRAME.HTM" TARGET="_top" onMouseOver = "self.status = 'Link to the articles by Herb Sutter'; return true" onMouseOut = "self.status = self.defaultStatus">Herb Sutter</A>.)<SCRIPT>create_link(3);</SCRIPT>
</P><A NAME="81635"> </A>
<P><A NAME="dingp4"></A>
We do know this much: programs that behave well in the presence of exceptions do so because they were <I>designed</I> to, not because they happen to. Exception-safe programs are not created by accident. The chances of a program behaving well in the presence of exceptions when it was not designed for exceptions are about the same as the chances of a program behaving well in the presence of multiple threads of control when it was not designed for multi-threaded execution: about <NOBR>zero.<SCRIPT>create_link(4);</SCRIPT>
</NOBR></P><A NAME="81513"> </A>
<P><A NAME="dingp5"></A>
That being the case, why use exceptions? Error codes have sufficed for C programmers ever since C was invented, so why mess with exceptions, especially if they're as problematic as I say? The answer is simple: exceptions cannot be ignored. If a function signals an exceptional condition by setting a status variable or returning an error code, there is no way to guarantee the function's caller will check the variable or examine the code. As a result, execution may continue long past the point where the condition was encountered. If the function signals the <A NAME="p45"></A>condition by throwing an exception, however, and that exception is not caught, program execution immediately <NOBR>ceases.<SCRIPT>create_link(5);</SCRIPT>
</NOBR></P>
<A NAME="81837"></A>
<P><A NAME="dingp6"></A>
This is behavior that C programmers can approach only by using <CODE>setjmp</CODE> and <CODE>longjmp</CODE>. But <CODE>longjmp</CODE> exhibits a serious deficiency when used with C++: it fails to call destructors for local objects when it adjusts the stack. Most C++ programs depend on such destructors being called, so <CODE>setjmp</CODE> and <CODE>longjmp</CODE> make a poor substitute for true exceptions. If you need a way of signaling exceptional conditions that cannot be ignored, and if you must ensure that local destructors are called when searching the stack for code that can handle exceptional conditions, you need C++ exceptions. It's as simple as <NOBR>that.<SCRIPT>create_link(6);</SCRIPT>
</NOBR></P><A NAME="81603"> </A>
<P><A NAME="dingp7"></A>
Because we have much to learn about programming with exceptions, the Items that follow comprise an incomplete guide to writing exception-safe software. Nevertheless, they introduce important considerations for anyone using exceptions in C++. By heeding the guidance in the material below (and in the <A HREF="../MAGAZINE/MA_FRAME.HTM" TARGET="_top" onMouseOver = "self.status = 'Link to magazine articles on this CD'; return true" onMouseOut = "self.status = self.defaultStatus">magazine articles on this CD</A>), you'll improve the correctness, robustness, and efficiency of the software you write, and you'll sidestep many problems that commonly arise when working with <NOBR>exceptions.<SCRIPT>create_link(7);</SCRIPT>
</NOBR></p>

<!-- SectionName="M9: Use destructors to prevent resource leaks" -->


<A NAME="5292"></A><DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MC3.HTM#71622">Exceptions</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MC3.HTM#38223">Item 10: Prevent resource leaks in constructors</A></FONT></DIV>
<P><A NAME="dingp8"></A><font ID="mititle">Item 9: &nbsp;Use destructors to prevent resource leaks.</font><SCRIPT>create_link(8);</SCRIPT>
</P>

<A NAME="72119"></A><A NAME="38385"></A>
<P><A NAME="dingp9"></A>
Say good-bye to pointers. Admit it: you never really liked them that much <NOBR>anyway.<SCRIPT>create_link(9);</SCRIPT>
</NOBR></P><A NAME="38394"></A>
<P><A NAME="dingp10"></A>
Okay, you don't have to say good-bye to <I>all</I> pointers, but you do need to say <I>sayonara</I> to pointers that are used to manipulate local resources. Suppose, for example, you're writing software at the Shelter for Adorable Little Animals, an organization that finds homes for puppies and kittens. Each day the shelter creates a file containing information on the adoptions it arranged that day, and your job is to write a program to read these files and do the appropriate processing for each <NOBR>adoption.<SCRIPT>create_link(10);</SCRIPT>
</NOBR></P><A NAME="69161"></A>
<P><A NAME="dingp11"></A>
A reasonable approach to this task is to define an abstract base class, <CODE>ALA</CODE> ("Adorable Little Animal"), plus concrete derived classes for puppies and kittens. A virtual function, <CODE>processAdoption</CODE>, handles the necessary species-specific <NOBR>processing:<SCRIPT>create_link(11);</SCRIPT>
</NOBR></P>

<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_045A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_045A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_045A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_045A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_045A5.GIF" BORDER=0></SPAN>

<SPAN ID="Image1of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_045A5.GIF" BORDER=0></SPAN>

<A NAME="69179"></A>
<UL><PRE><A NAME="p46"></A>class ALA {
public:
  virtual void processAdoption() = 0;
  ...
};
</PRE>
</UL><A NAME="69182"></A>
<UL><PRE>class Puppy: public ALA {
public:
  virtual void processAdoption();
  ...
};
</PRE>
</UL><A NAME="69190"></A>
<UL><PRE>class Kitten: public ALA {
public:
  virtual void processAdoption();
  ...
};
</PRE>
</UL><A NAME="69188"></A>
<P><A NAME="dingp12"></A>
You'll need a function that can read information from a file and produce either a <CODE>Puppy</CODE> object or a <CODE>Kitten</CODE> object, depending on the information in the file. This is a perfect job for a <I>virtual constructor</I>, a kind of function described in <A HREF="./MC5_FR.HTM#5341" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 25'; return true" onMouseOut = "self.status = self.defaultStatus">Item 25</A>. For our purposes here, the function's declaration is all we <NOBR>need:<SCRIPT>create_link(12);</SCRIPT>
</NOBR></p>
<A NAME="69233"></A>
<UL><PRE>// read animal information from s, then return a pointer
// to a newly allocated object of the appropriate type
ALA * readALA(istream&amp; s);
</PRE>
</UL><A NAME="69229"></A>
<P><A NAME="dingp13"></A>
The heart of your program is likely to be a function that looks something like <NOBR>this:<SCRIPT>create_link(13);</SCRIPT>
</NOBR></p>

<A NAME="69239"></A>
<UL><PRE>void processAdoptions(istream&amp; dataSource)
{
  while (dataSource) {                  // while there's data
    ALA *pa = readALA(dataSource);      // get next animal
    pa-&gt;processAdoption();              // process adoption
    delete pa;                          // delete object that
  }                                     // readALA returned
}
</PRE>
</UL>

<A NAME="69180"></A>
<P><A NAME="dingp14"></A>
This function loops through the information in <CODE>dataSource</CODE>, processing each entry as it goes. The only mildly tricky thing is the need to remember to delete <CODE>pa</CODE> at the end of each iteration. This is necessary because <CODE>readALA</CODE> creates a new heap object each time it's called. Without the call to <CODE>delete</CODE>, the loop would contain a resource <NOBR>leak.<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P><A NAME="69244"></A>
<P><A NAME="dingp15"></A>
Now consider what would happen if <CODE>pa-&gt;processAdoption</CODE> threw an exception. <CODE>processAdoptions</CODE> fails to catch exceptions, so the exception would propagate to <CODE>processAdoptions</CODE>'s caller. In doing so, all statements in <CODE>processAdoptions</CODE> after the call to <CODE>pa-&gt;processAdoption</CODE> would be skipped, and that means <CODE>pa</CODE> would never be deleted. As <A NAME="p47"></A>a result, anytime <CODE>pa-&gt;processAdoption</CODE> throws an exception, <CODE>processAdoptions</CODE> contains a resource <NOBR>leak.<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P><A NAME="38462"></A>
<P><A NAME="dingp16"></A>
Plugging the leak is easy <NOBR>enough,<SCRIPT>create_link(16);</SCRIPT>
</NOBR></p>
<A NAME="69309"></A>
<UL><PRE>void processAdoptions(istream&amp; dataSource)
{
  while (dataSource) {
    ALA *pa = readALA(dataSource);
</PRE>
</UL><A NAME="69316"></A>
<UL><PRE>    try {
      pa-&gt;processAdoption();
    }
    catch (...) {            // catch all exceptions
</PRE>
</UL><A NAME="69364"></A>
<UL><PRE>
      delete pa;             // avoid resource leak when an
                             // exception is thrown
</PRE>
</UL><A NAME="69326"></A>
<UL><PRE>
      throw;                 // propagate exception to caller
    }
</PRE>
</UL><A NAME="69332"></A>
<UL><PRE>
  delete pa;                 // avoid resource leak when no
  }                          // exception is thrown
}
</PRE>
</UL><A NAME="38463"></A><p><A NAME="dingp17"></A>
but then you have to litter your code with <CODE>try</CODE> and <CODE>catch</CODE> blocks. More importantly, you are forced to duplicate cleanup code that is common to both normal and exceptional paths of control. In this case, the call to <CODE>delete</CODE> must be duplicated. Like all replicated code, this is annoying to write and difficult to maintain, but it also <I>feels wrong</I>. Regardless of whether we leave <CODE>processAdoptions</CODE> by a normal return or by throwing an exception, we need to delete <CODE>pa</CODE>, so why should we have to say that in more than one <NOBR>place?<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P><A NAME="38503"></A>
<P><A NAME="dingp18"></A>
We don't have to if we can somehow move the cleanup code that must always be executed into the destructor for an object local to <CODE>processAdoptions</CODE>. That's because local objects are always destroyed when leaving a function, regardless of how that function is exited. (The only exception to this rule is when you call <CODE>longjmp</CODE>, and this shortcoming of <CODE>longjmp</CODE> is the primary reason why C++ has support for exceptions in the first place.) Our real concern, then, is moving the <CODE>delete</CODE> from <CODE>processAdoptions</CODE> into a destructor for an object local to <CODE>processAdoptions</CODE>.<SCRIPT>create_link(18);</SCRIPT>
</P><A NAME="38535"></A>
<P><A NAME="dingp19"></A>
The solution is to replace the pointer <CODE>pa</CODE> with an object that <I>acts like</I> a pointer. That way, when the pointer-like object is (automatically) destroyed, we can have its destructor call <CODE>delete</CODE>. Objects that act like pointers, but do more, are called <I>smart pointers</I>, and, as <A HREF="./MC5_FR.HTM#61766" TARGET="_top" onMouseOver = "self.status = 'Link to MEC++ Item 28'; return true" onMouseOut = "self.status = self.defaultStatus">Item 28</A> explains, you can make pointer-like objects very smart indeed. In this case, we don't need a particularly brainy pointer, we just need a <A NAME="p48"></A>pointer-like object that knows enough to delete what it points to when the pointer-like object goes out of <NOBR>scope.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P><A NAME="69374"></A>
<P><A NAME="dingp20"></A>
It's not difficult to write a class for such objects, but we don't need to. The standard C++ library (see <A HREF="../EC/EC7_FR.HTM#8392" TARGET="_top" onMouseOver = "self.status = 'Link to EC++ Item 49'; return true" onMouseOut = "self.status = self.defaultStatus">Item E49</A>) contains a class template called <CODE>auto_ptr</CODE> that does just what we want. Each <CODE>auto_ptr</CODE> class takes a pointer to a heap object in its constructor and deletes that object in its destructor. Boiled down to these essential functions, <CODE>auto_ptr</CODE> looks like <NOBR>this:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></p><A NAME="69389"></A>
<UL><PRE>template&lt;class T&gt;
class auto_ptr {
public:
  auto_ptr(T *p = 0): ptr(p) {}        // save ptr to object
  ~auto_ptr() { delete ptr; }          // delete ptr to object
</PRE>
</UL><A NAME="69421"></A>
<UL><PRE>private:
  T *ptr;                              // raw ptr to object

⌨️ 快捷键说明

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