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

📄 mi28.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
auto_ptr<TreeNode> ptn2 = ptn1;      // call to copy ctor;
                                     // what should happen?
<A NAME="61838"></A>
<A NAME="p163"></A>auto_ptr&lt;TreeNode&gt; ptn3;
<A NAME="61839"></A>
ptn3 = ptn2;                         // call to operator=;
                                     // what should happen?
</PRE>
</UL>

<A NAME="61840"></A>
<P><A NAME="dingp19"></A>
If we just copied the internal dumb pointer, we'd end up with two <CODE>auto_ptr</CODE>s pointing to the same object. This would lead to grief, because each <CODE>auto_ptr</CODE> would delete what it pointed to when the <CODE>auto_ptr</CODE> was destroyed. That would mean we'd delete an object more than once. The results of such double-deletes are undefined (and are frequently <NOBR>disastrous).<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P><A NAME="70964"></A>
<P><A NAME="dingp20"></A>
An alternative would be to create a new copy of what was pointed to by calling <CODE>new</CODE>. That would guarantee we didn't have too many <CODE>auto_ptr</CODE>s pointing to a single object, but it might engender an unacceptable performance hit for the creation (and later destruction) of the new object. Furthermore, we wouldn't necessarily know what type of object to create, because an <CODE>auto_ptr&lt;T&gt;</CODE> object need not point to an object of type <CODE>T</CODE>; it might point to an object of a type <I>derived</I> from <CODE>T</CODE>. Virtual constructors (see <A HREF="./MI25_FR.HTM#5341" TARGET="_top">Item 25</A>) can help solve this problem, but it seems inappropriate to require their use in a general-purpose class like <CODE>auto_ptr</CODE>.<SCRIPT>create_link(20);</SCRIPT>
</P><A NAME="61841"></A>
<P><A NAME="dingp21"></A>
The problems would vanish if <CODE>auto_ptr</CODE> prohibited copying and assignment, but a more flexible solution was adopted for the <CODE>auto_ptr</CODE> classes: object ownership is <I>transferred</I> when an <CODE>auto_ptr</CODE> is copied or <NOBR>assigned:<SCRIPT>create_link(21);</SCRIPT>
</NOBR></P>
<A NAME="61842"></A>
<UL><PRE>template&lt;class T&gt;
class auto_ptr {
public:
  ...
<A NAME="9971"></A>
  auto_ptr(auto_ptr&lt;T&gt;&amp; rhs);        // copy constructor
<A NAME="80383"></A>
  auto_ptr&lt;T&gt;&amp;                       // assignment
  operator=(auto_ptr&lt;T&gt;&amp; rhs);       // operator
<A NAME="9972"></A>
  ...
};
<A NAME="61843"></A>
template&lt;class T&gt;
auto_ptr&lt;T&gt;::auto_ptr(auto_ptr&lt;T&gt;&amp; rhs)
{
  pointee = rhs.pointee;             // transfer ownership of
                                     // *pointee to *this
<A NAME="61844"></A>
  rhs.pointee = 0;                   // rhs no longer owns
}                                    // anything
<A NAME="61845"></A>
<A NAME="p164"></A>template&lt;class T&gt;
auto_ptr&lt;T&gt;&amp; auto_ptr&lt;T&gt;::operator=(auto_ptr&lt;T&gt;&amp; rhs)
{
  if (this == &amp;rhs)                  // do nothing if this
    return *this;                    // object is being assigned
                                     // to itself
<A NAME="72309"></A>
  delete pointee;                    // delete currently owned
                                     // object
<A NAME="72307"></A>
  pointee = rhs.pointee;             // transfer ownership of
  rhs.pointee = 0;                   // *pointee from rhs to *this
<A NAME="75840"></A>
  return *this;
}
</PRE>
</UL>

<A NAME="61846"></A>
<P><A NAME="dingp22"></A>
Notice that the assignment operator must delete the object it owns before assuming ownership of a new object. If it failed to do this, the object would never be deleted. Remember, nobody but the <CODE>auto_ptr</CODE> object owns the object the <CODE>auto_ptr</CODE> points <NOBR>to.<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P><A NAME="80396"></A>
<P><A NAME="dingp23"></A>
Because object ownership is transferred when <CODE>auto_ptr</CODE>'s copy constructor is called, passing <CODE>auto_ptr</CODE>s by value is often a <i>very</i> bad idea. Here's why:
<A NAME="80424"></A><SCRIPT>create_link(23);</SCRIPT>
</P>
<UL><PRE>// this function will often lead to disaster
void printTreeNode(ostream&amp; s, auto_ptr&lt;TreeNode&gt; p)
{ s &lt;&lt; *p; }
<A NAME="80403"></A>
int main()
{
  auto_ptr&lt;TreeNode&gt; ptn(new TreeNode);
<A NAME="80406"></A>
  ...
<A NAME="80407"></A>
  printTreeNode(cout, ptn);          // pass auto_ptr by value
<A NAME="80427"></A>
  ...
<A NAME="80411"></A>
}
</PRE>
</UL><A NAME="89556"></A>
<P><A NAME="dingp24"></A>
When <CODE>printTreeNode</CODE>'s parameter <CODE>p</CODE> is initialized (by calling <CODE>auto_ptr</CODE>'s copy constructor), ownership of the object pointed to by <CODE>ptn</CODE> is transferred to <CODE>p</CODE>. When <CODE>printTreeNode</CODE> finishes executing, <CODE>p</CODE> goes out of scope and its destructor deletes what it points to (which is what <CODE>ptn</CODE> used to point to). <CODE>ptn</CODE>, however, no longer points to anything (its underlying dumb pointer is null), so just about any attempt to use it after the call to <CODE>printTreeNode</CODE> will yield undefined behavior. Passing <CODE>auto_ptr</CODE>s by value, then, is something to be done only if you're <I>sure</I> you want to transfer ownership of an object to a (transient) function parameter. Only rarely will you want to do <NOBR>this.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P><A NAME="80517"></A>
<P><A NAME="dingp25"></A>
<A NAME="p165"></A>This doesn't mean you can't pass <CODE>auto_ptr</CODE>s as parameters, it just means that pass-by-value is not the way to do it. Pass-by-reference-to-<CODE>const</CODE> <NOBR>is:<SCRIPT>create_link(25);</SCRIPT>
</NOBR></P>
<A NAME="80520"></A>
<UL><PRE>// this function behaves much more intuitively
void printTreeNode(ostream&amp; s,
                   const auto_ptr&lt;TreeNode&gt;&amp; p)
{ s &lt;&lt; *p; }
</PRE>
</UL><A NAME="80518"></A>
<P><A NAME="dingp26"></A>
In this function, <CODE>p</CODE> is a reference, not an object, so no constructor is called to initialize <CODE>p</CODE>. When <CODE>ptn</CODE> is passed to this version of <CODE>printTreeNode</CODE>, it retains ownership of the object it points to, and <CODE>ptn</CODE> can safely be used after the call to <CODE>printTreeNode</CODE>. Thus, passing <CODE>auto_ptr</CODE>s by reference-to-<CODE>const</CODE> avoids the hazards arising from pass-by-value. (For other reasons to prefer pass-by-reference to pass-by-value, check out <A HREF="../EC/EI22_FR.HTM#6133" TARGET="_top">Item E22</A>.)<SCRIPT>create_link(26);</SCRIPT>
</P><A NAME="72308"></A>
<P><A NAME="dingp27"></A>
The notion of transferring ownership from one smart pointer to another during copying and assignment is interesting, but you may have been at least as interested in the unconventional declarations of the copy constructor and assignment operator. These functions normally take <CODE>const</CODE> parameters, but above they do not. In fact, the code above <I>changes</I> these parameters during the copy or the assignment. In other words, <CODE>auto_ptr</CODE> objects are modified if they are copied or are the source of an <NOBR>assignment!<SCRIPT>create_link(27);</SCRIPT>
</NOBR></P><A NAME="61847"></A>
<P><A NAME="dingp28"></A>
Yes, that's exactly what's happening. Isn't it nice that C++ is flexible enough to let you do this? If the language required that copy constructors and assignment operators take <CODE>const</CODE> parameters, you'd probably have to cast away the parameters' <CODE>const</CODE>ness (see <A HREF="../EC/EI21_FR.HTM#6003" TARGET="_top">Item E21</A>) or play other games to implement ownership transferral. Instead, you get to say exactly what you want to say: when an object is copied or is the source of an assignment, that object is changed. This may not seem intuitive, but it's simple, direct, and, in this case, <NOBR>accurate.<SCRIPT>create_link(28);</SCRIPT>
</NOBR></P><A NAME="71698"></A>
<P><A NAME="dingp29"></A>
If you find this examination of <CODE>auto_ptr</CODE> member functions interesting, you may wish to see a complete implementation. You'll find one on pages <a href="./MIAUTOFR.HTM#73912" TARGET="_top">291</A>-<a href="./MIAUTOFR.HTM#p294" TARGET="_top">294</A>, where you'll also see that the <CODE>auto_ptr</CODE> template in the standard C++ library has copy constructors and assignment operators that are more flexible than those described here. In the standard <CODE>auto_ptr</CODE> template, those functions are member function <I>templates</I>, not just member functions. (Member function templates are described later in this Item. You can also read about them in <A HREF="../EC/EI25_FR.HTM#6292" TARGET="_top">Item E25</A>.)<SCRIPT>create_link(29);</SCRIPT>
</P><A NAME="61853"></A>
<P><A NAME="dingp30"></A>
A smart pointer's destructor often looks like this:
<A NAME="61854"></A><SCRIPT>create_link(30);</SCRIPT>
</P>
<UL><PRE><A NAME="p166"></A>template&lt;class T&gt;
SmartPtr&lt;T&gt;::~SmartPtr()
{
  if (<I>*this owns *pointee</I>) {
    delete pointee;
  }
}
</PRE>
</UL><A NAME="61855"></A>

<P><A NAME="dingp31"></A>Sometimes there is no need for the test. An <CODE>auto_ptr</CODE> always owns what it points to, for example. At other times the test is a bit more complicated. A smart pointer that employs reference counting (see <A HREF="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>) must adjust a reference count before determining whether it has the right to delete what it points to. Of course, some smart pointers are like dumb pointers: they have no effect on the object they point to when they themselves are <NOBR>destroyed.<SCRIPT>create_link(31);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp32"></A><FONT ID="mhtitle">Implementing the Dereferencing Operators</FONT><SCRIPT>create_link(32);</SCRIPT>
</P>
<A NAME="61860"></A>

<P><A NAME="dingp33"></A>Let us now turn our attention to the very heart of smart pointers, the <CODE>operator*</CODE> and <CODE>operator-&gt;</CODE> functions. The former returns the object pointed to. Conceptually, this is <NOBR>simple:<SCRIPT>create_link(33);</SCRIPT>
</NOBR></P>
<A NAME="61861"></A>

<UL><PRE>template&lt;class T&gt;
T&amp; SmartPtr&lt;T&gt;::operator*() const
{
  <I>perform "smart pointer" processing;</I>
<A NAME="61862"></A>
  return *pointee;
}
</PRE>
</UL><A NAME="61865"></A>
<P><A NAME="dingp34"></A>First the function does whatever processing is needed to initialize or otherwise make <CODE>pointee</CODE> valid. For example, if lazy fetching is being used (see <A HREF="./MI17_FR.HTM#41011" TARGET="_top">Item 17</A>), the function may have to conjure up a new object for <CODE>pointee</CODE> to point to. Once <CODE>pointee</CODE> is valid, the <CODE>operator*</CODE> function just returns a reference to the pointed-to <NOBR>object.<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>
<A NAME="61869"></A>
<P><A NAME="dingp35"></A>
Note that the return type is a <I>reference</I>. It would be disastrous to return an <I>object</I> instead, though compilers will let you do it. Bear in mind that <CODE>pointee</CODE> need not point to an object of type <CODE>T</CODE>; it may point to an object of a class <I>derived</I> from <CODE>T</CODE>. If that is the case and your <CODE>operator* </CODE>function returns a <CODE>T</CODE> object instead of a reference to the actual derived class object, your function will return an object of the wrong type! (This is the <I>slicing problem</I>. See <A HREF="../EC/EI22_FR.HTM#6133" TARGET="_top">Item E22</A> and <A HREF="./MI13_FR.HTM#38224" TARGET="_top">Item 13</A>.) Virtual functions invoked on the object returned from your star-crossed <CODE>operator*</CODE> will not invoke the function corresponding to the <A HREF="./MIINTRFR.HTM#72671" TARGET="_top">dynamic type</a> of the pointed-to object. In essence, your smart pointer will not properly support virtual functions, and how smart is a pointer like that? Besides, returning a reference is more efficient anyway, because there is no need to construct a temporary object (see <A HREF="./MI19_FR.HTM#41177" TARGET="_top">Item 19</A>). This is one of <A NAME="p167"></A>those happy occasions when correctness and efficiency go hand in <NOBR>hand.<SCRIPT>create_link(35);</SCRIPT>
</NOBR></P><A NAME="61873"></A>
<P><A NAME="dingp36"></A>
If you're the kind who likes to worry, you may wonder what you should do if somebody invokes <CODE>operator*</CODE> on a null smart pointer, i.e., one whose embedded dumb pointer is null. Relax. You can do anything you want. The result of dereferencing a null pointer is undefined, so there is no "wrong" behavior. Wanna throw an exception? Go ahead, throw it. Wanna call <CODE>abort</CODE> (possibly by having an <CODE>assert</CODE> call fail)? Fine, call it. Wanna walk through memory setting every byte to your birth date modulo 256? That's okay, too. It's not nice, but as far as the language is concerned, you are completely <NOBR>unfettered.<SCRIPT>create_link(36);</SCRIPT>
</NOBR></P><A NAME="61874"></A>
<P><A NAME="dingp37"></A>
The story with <CODE>operator-&gt;</CODE> is similar to that for <CODE>operator*</CODE>, but before examining <CODE>operator-&gt;</CODE>, let us remind ourselves of the unusual meaning of a call to this function. Consider again the <CODE>editTuple</CODE> function that uses a smart pointer-to-<CODE>Tuple</CODE> <NOBR>object:<SCRIPT>create_link(37);</SCRIPT>
</NOBR></P>
<A NAME="61875"></A>
<UL><PRE>void editTuple(DBPtr&lt;Tuple&gt;&amp; pt)
{
  LogEntry&lt;Tuple&gt; entry(*pt);
<A NAME="75850"></A>
  do {
    pt-&gt;displayEditDialog();
  } while (pt-&gt;isValid() == false);
}
</PRE>
</UL><A NAME="61876"></A>
<P><A NAME="dingp38"></A>
The <NOBR>statement<SCRIPT>create_link(38);</SCRIPT>
</NOBR></P>
<A NAME="61877"></A>
<UL><PRE>pt-&gt;displayEditDialog();</PRE>
</UL>
<A NAME="61878"></A>
<A NAME="dingp39"></A>
is interpreted by compilers as:<SCRIPT>create_link(39);</SCRIPT>

<A NAME="61879"></A>
<UL><PRE>(pt.operator-&gt;())-&gt;displayEditDialog();</PRE>
</UL>
<A NAME="61880"></A>
<P><A NAME="dingp40"></A>
That means that whatever <CODE>operator-&gt;</CODE> returns, it must be legal to apply the member-selection operator (<CODE>-&gt;</CODE>) to it. There are thus only two things <CODE>operator-&gt;</CODE> can return: a dumb pointer to an object or another smart pointer object. Most of the time, you'll want to return an ordinary dumb pointer. In those cases, you implement <CODE>operator-&gt;</CODE> as <NOBR>follows:<SCRIPT>create_link(40);</SCRIPT>
</NOBR></P>
<A NAME="61882"></A>
<UL><PRE>template&lt;class T&gt;

⌨️ 快捷键说明

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