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

📄 mi10.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
</PRE>
</UL><A NAME="39100"></A>
<P><A NAME="dingp16"></A>
There is no need to worry about <CODE>BookEntry</CODE>'s non-pointer data members. Data members are automatically initialized before a class's constructor is called, so if a <CODE>BookEntry</CODE> constructor body begins executing, the object's <CODE>theName</CODE>, <CODE>theAddress</CODE>, and <CODE>thePhones</CODE> data members have already been fully constructed. As fully constructed objects, these data members will be automatically destroyed when the <CODE>BookEntry</CODE> object containing them is, and there is no need for you to intervene. Of course, if these objects' constructors call functions that might throw exceptions, <I>those</I> constructors have to worry about catching the exceptions and performing any necessary cleanup before allowing them to <NOBR>propagate.<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P><A NAME="39092"></A>
<P><A NAME="dingp17"></A>
You may have noticed that the statements in <CODE>BookEntry</CODE>'s <CODE>catch</CODE> block are almost the same as those in <CODE>BookEntry</CODE>'s destructor. Code duplication here is no more tolerable than it is anywhere else, so the best way to structure things is to move the common code into a private helper function and have both the constructor and the destructor call <NOBR>it:<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="39133"></A>
<UL><PRE>class BookEntry {
public:
  ...                      // as before
<A NAME="57468"></A>
private:
  ...
  void cleanup();          // common cleanup statements
};
<A NAME="39159"></A>
<A NAME="p55"></A>void BookEntry::cleanup()
{
  delete theImage;
  delete theAudioClip;
}
<A NAME="70158"></A>
BookEntry::BookEntry(const string&amp; name,
                     const string&amp; address,
         	     const string&amp; imageFileName,
          	     const string&amp; audioClipFileName)
: theName(name), theAddress(address),
  theImage(0), theAudioClip(0)
{
  try {
    ...                   // as before
  }
  catch (...)   {
    cleanup();            // release resources
    throw;                // propagate exception
  }
}
<A NAME="39165"></A>
BookEntry::~BookEntry()
{
  cleanup();
}
</PRE>
</UL><A NAME="39157"></A>
<P><A NAME="dingp18"></A>
This is nice, but it doesn't put the topic to rest. Let us suppose we design our <CODE>BookEntry</CODE> class slightly differently so that <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> are <I>constant</I> <NOBR>pointers:<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="70192"></A>
<UL><PRE>class BookEntry {
public:
  ...                                  // as above
<A NAME="39225"></A>
private:
  ...
  Image * const theImage;              // pointers are now
  AudioClip * const theAudioClip;      // const
};
</PRE>
</UL><A NAME="39253"></A>
<P><A NAME="dingp19"></A>
Such pointers must be initialized via the member initialization lists of <CODE>BookEntry</CODE>'s constructors, because there is no other way to give <CODE>const</CODE> pointers a value (see <a href="../EC/EI12_FR.HTM#2071" TARGET="_top">Item E12</A>). A common temptation is to initialize <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> like <NOBR>this,<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>

<A NAME="39262"></A>
<UL><PRE><A NAME="p56"></A>// an implementation that may leak resources if an
// exception is thrown
BookEntry::BookEntry(const string&amp; name,
                     const string&amp; address,
                     const string&amp; imageFileName,
                     const string&amp; audioClipFileName)
: theName(name), theAddress(address),
  theImage(imageFileName != ""
        ? new Image(imageFileName)
        : 0),
  theAudioClip(audioClipFileName != ""
          ? new AudioClip(audioClipFileName)
          : 0)
{}
</PRE>
</UL><A NAME="39258"></A>
<P><A NAME="dingp20"></A>but this leads to the problem we originally wanted to eliminate: if an exception is thrown during initialization of <CODE>theAudioClip</CODE>, the object pointed to by <CODE>theImage</CODE> is never destroyed. Furthermore, we can't solve the problem by adding <CODE>try</CODE> and <CODE>catch</CODE> blocks to the constructor, because <CODE>try</CODE> and <CODE>catch</CODE> are statements, and member initialization lists allow only expressions. (That's why we had to use the <CODE>?:</CODE> syntax instead of the <CODE>if</CODE>-<CODE>then</CODE>-<CODE>else</CODE> syntax in the initialization of <CODE>theImage</CODE> and <CODE>theAudioClip</CODE>.)<SCRIPT>create_link(20);</SCRIPT>
</P>
<A NAME="39317"></A>
<P><A NAME="dingp21"></A>
Nevertheless, the only way to perform cleanup chores before exceptions propagate out of a constructor is to catch those exceptions, so if we can't put <CODE>try</CODE> and <CODE>catch</CODE> in a member initialization list, we'll have to put them somewhere else. One possibility is inside private member functions that return pointers with which <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> should be <NOBR>initialized:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="70261"></A>
<UL><PRE>class BookEntry {
public:
  ...                     // as above
<A NAME="70262"></A>
private:
  ...                     // data members as above
<A NAME="70265"></A>
Image * initImage(const string&amp; imageFileName);
  AudioClip * initAudioClip(const string&amp;
                            audioClipFileName);
};
<A NAME="44136"></A>
BookEntry::BookEntry(const string&amp; name,
                     const string&amp; address,
                     const string&amp; imageFileName,
                     const string&amp; audioClipFileName)
: theName(name), theAddress(address),
  theImage(initImage(imageFileName)),
  theAudioClip(initAudioClip(audioClipFileName))
{}
<A NAME="39348"></A>
<A NAME="p57"></A>// theImage is initialized first, so there is no need to
// worry about a resource leak if this initialization
// fails. This function therefore handles no exceptions
Image * BookEntry::initImage(const string&amp; imageFileName)
{
  if (imageFileName != "") return new Image(imageFileName);
  else return 0;
}
<A NAME="39349"></A>
// theAudioClip is initialized second, so it must make
// sure theImage's resources are released if an exception
// is thrown during initialization of theAudioClip. That's
// why this function uses try...catch.
AudioClip * BookEntry::initAudioClip(const string&amp;
                                     audioClipFileName)
{
  try {
    if (audioClipFileName != "") {
      return new AudioClip(audioClipFileName);
    }
    else return 0;
  }
  catch (...)   {
    delete theImage;
    throw;
  }
}
</PRE>
</UL><A NAME="39296"></A>
<P><A NAME="dingp22"></A>
This is perfectly kosher, and it even solves the problem we've been laboring to overcome. The drawback is that code that conceptually belongs in a constructor is now dispersed across several functions, and that's a maintenance <NOBR>headache.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P><A NAME="69830"></A>
<P><A NAME="dingp23"></A>
A better solution is to adopt the advice of <a href="./MI9_FR.HTM#5292" TARGET="_top">Item 9</A> and treat the objects pointed to by <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> as resources to be managed by local objects. This solution takes advantage of the facts that both <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> are pointers to dynamically allocated objects and that those objects should be deleted when the pointers themselves go away. This is precisely the set of conditions for which the <CODE>auto_ptr</CODE> classes (see <a href="./MI9_FR.HTM#5292" TARGET="_top">Item&nbsp;9</A>) were designed. We can therefore change the raw pointer types of <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> to their <CODE>auto_ptr</CODE> <NOBR>equivalents:<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="70365"></A>
<UL><PRE>class BookEntry {
public:
  ...                                      // as above
<A NAME="70366"></A>
private:
  ...
  const auto_ptr&lt;Image&gt; theImage;          // these are now
  const auto_ptr&lt;AudioClip&gt; theAudioClip;  // auto_ptr objects
};
</PRE>
</UL><A NAME="44030"></A>
<A NAME="p58"></A><P><A NAME="dingp24"></A>
Doing this makes <CODE>BookEntry</CODE>'s constructor leak-safe in the presence of exceptions, and it lets us initialize <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> using the member initialization <NOBR>list:<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<A NAME="70386"></A>
<UL><PRE>
BookEntry::BookEntry(const string&amp; name,
                     const string&amp; address,
                     const string&amp; imageFileName,
                     const string&amp; audioClipFileName)
: theName(name), theAddress(address),
  theImage(imageFileName != ""
        ? new Image(imageFileName)
        : 0),
  theAudioClip(audioClipFileName != ""
          ? new AudioClip(audioClipFileName)
          : 0)
{}
</PRE>
</UL><A NAME="44041"></A>
<P><A NAME="dingp25"></A>
In this design, if an exception is thrown during initialization of <CODE>theAudioClip</CODE>, <CODE>theImage</CODE> is already a fully constructed object, so it will automatically be destroyed, just like <CODE>theName</CODE>, <CODE>theAddress</CODE>, and <CODE>thePhones</CODE>. Furthermore, because <CODE>theImage</CODE> and <CODE>theAudioClip</CODE> are now objects, they'll be destroyed automatically when the <CODE>BookEntry</CODE> object containing them is. Hence there's no need to manually delete what they point to. That simplifies <CODE>BookEntry</CODE>'s destructor <NOBR>considerably:<SCRIPT>create_link(25);</SCRIPT>
</NOBR></P>
<A NAME="44025"></A>
<UL><PRE>BookEntry::~BookEntry()
{}                                      // nothing to do!
</PRE>
</UL><A NAME="70404"></A>
<P><A NAME="dingp26"></A>
This means you could eliminate <CODE>BookEntry</CODE>'s destructor <NOBR>entirely.<SCRIPT>create_link(26);</SCRIPT>
</NOBR></P>
<A NAME="43984"></A>
<P><A NAME="dingp27"></A>
It all adds up to this: if you replace pointer class members with their corresponding <CODE>auto_ptr</CODE> objects, you fortify your constructors against resource leaks in the presence of exceptions, you eliminate the need to manually deallocate resources in destructors, and you allow <CODE>const</CODE> member pointers to be handled in the same graceful fashion as non-<CODE>const</CODE> <NOBR>pointers.<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P><A NAME="49081"></A>
<P><A NAME="dingp28"></A>
Dealing with the possibility of exceptions during construction can be tricky, but <CODE>auto_ptr</CODE> (and <CODE>auto_ptr</CODE>-like classes) can eliminate most of the drudgery. Their use leaves behind code that's not only easy to understand, it's robust in the face of exceptions, <NOBR>too.<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./MI9_FR.HTM" TARGET="_top">Item 9: Use destructors to prevent resource leaks</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./MI11_FR.HTM" TARGET="_top">Item 11: Prevent exceptions from leaving destructors</A></FONT></DIV>

</BODY>
</HTML>

⌨️ 快捷键说明

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