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

📄 mi28.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
  operator T*() { return pointee; }
  ...
};
<A NAME="61949"></A>
DBPtr&lt;Tuple&gt; pt;
<A NAME="61950"></A>
...
<A NAME="61951"></A>
normalize(pt);                       // this now works
</PRE>
</UL><A NAME="61952"></A>
<P><A NAME="dingp62"></A>
Addition of this function also eliminates the problem of testing for <NOBR>nullness:<SCRIPT>create_link(62);</SCRIPT>
</NOBR></P>
<A NAME="61953"></A>
<UL><PRE>if (pt == 0) ...                     // fine, converts pt to a
                                     // Tuple*
<A NAME="61954"></A>
if (pt) ...                          // ditto
<A NAME="61955"></A>
if (!pt) ...                         // ditto (reprise)
</PRE>
</UL><A NAME="61956"></A>
<P><A NAME="dingp63"></A>
However, there is a dark side to such conversion functions. (There almost always is. Have you been seeing <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>?) They make it easy for clients to program directly with dumb pointers, thus bypassing the smarts your pointer-like objects are designed to <NOBR>provide:<SCRIPT>create_link(63);</SCRIPT>
</NOBR></P>
<A NAME="61960"></A>
<UL><PRE>void processTuple(DBPtr&lt;Tuple&gt;&amp; pt)
{
  Tuple *rawTuplePtr = pt;           // converts DBPtr&lt;Tuple&gt; to
                                     // Tuple*
<A NAME="61961"></A>
  <I>use rawTuplePtr to modify the tuple;</I>
<A NAME="61962"></A>
}
</PRE>
</UL><A NAME="61963"></A>
<P><A NAME="dingp64"></A>
Usually, the "smart" behavior provided by a smart pointer is an essential component of your design, so allowing clients to use dumb pointers typically leads to disaster. For example, if <CODE>DBPtr</CODE> implements the reference-counting strategy of <A HREF="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>, allowing clients to manipulate dumb pointers directly will almost certainly lead to bookkeeping errors that corrupt the reference-counting data <NOBR>structures.<SCRIPT>create_link(64);</SCRIPT>
</NOBR></P><A NAME="71086"></A>
<P><A NAME="dingp65"></A>
<A NAME="p172"></A>Even if you provide an implicit conversion operator to go from a smart pointer to the dumb pointer it's built on, your smart pointer will never be truly interchangeable with the dumb pointer. That's because the conversion from a smart pointer to a dumb pointer is a user-defined conversion, and compilers are forbidden from applying more than one such conversion at a time. For example, suppose you have a class representing all the clients who have accessed a particular <NOBR>tuple:<SCRIPT>create_link(65);</SCRIPT>
</NOBR></P>
<A NAME="71141"></A>
<UL><PRE>class TupleAccessors {
public:
  TupleAccessors(const Tuple *pt);   // pt identifies the
  ...                                // tuple whose accessors
};                                   // we care about
</PRE>
</UL><A NAME="71142"></A>
<P><A NAME="dingp66"></A>
As usual, <CODE>TupleAccessors</CODE>' single-argument constructor also acts as a type-conversion operator from <CODE>Tuple*</CODE> to <CODE>TupleAccessors</CODE> (see <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>). Now consider a function for merging the information in two <CODE>TupleAccessors</CODE> <NOBR>objects:<SCRIPT>create_link(66);</SCRIPT>
</NOBR></P>
<A NAME="71161"></A>
<UL><PRE>TupleAccessors merge(const TupleAccessors&amp; ta1,
                     const TupleAccessors&amp; ta2);
</PRE>
</UL><A NAME="71055"></A>
<P><A NAME="dingp67"></A>
Because a <CODE>Tuple*</CODE> may be implicitly converted to a <CODE>TupleAccessors</CODE>, calling <CODE>merge</CODE> with two dumb <CODE>Tuple*</CODE> pointers is <NOBR>fine:<SCRIPT>create_link(67);</SCRIPT>
</NOBR></P><A NAME="71166"></A>
<UL><PRE>Tuple *pt1, *pt2;
<A NAME="75877"></A>
...
<A NAME="75878"></A>
merge(pt1, pt2);                     // fine, both pointers are converted
                                     // to TupleAccessors objects
</PRE>
</UL><A NAME="71170"></A>
<P><A NAME="dingp68"></A>
The corresponding call with smart <CODE>DBPtr&lt;Tuple&gt;</CODE> pointers, however, fails to <NOBR>compile:<SCRIPT>create_link(68);</SCRIPT>
</NOBR></P>
<A NAME="71177"></A>
<UL><PRE>DBPtr&lt;Tuple&gt; pt1, pt2;
<A NAME="75879"></A>
...
<A NAME="75880"></A>
merge(pt1, pt2);                     // error! No way to convert pt1 and
                                     // pt2 to TupleAccessors objects
</PRE>
</UL><A NAME="71175"></A>
<P><A NAME="dingp69"></A>
That's because a conversion from <CODE>DBPtr&lt;Tuple&gt;</CODE> to <CODE>TupleAccessors</CODE> calls for <I>two</I> user-defined conversions (one from <CODE>DBPtr&lt;Tuple&gt;</CODE> to <CODE>Tuple*</CODE> and one from <CODE>Tuple*</CODE> to <CODE>TupleAccessors</CODE>), and such sequences of conversions are prohibited by the <NOBR>language.<SCRIPT>create_link(69);</SCRIPT>
</NOBR></P><A NAME="61967"></A>
<P><A NAME="dingp70"></A>
Smart pointer classes that provide an implicit conversion to a dumb pointer open the door to a particularly nasty bug. Consider this <NOBR>code:<SCRIPT>create_link(70);</SCRIPT>
</NOBR></P>
<A NAME="61968"></A>
<UL><PRE>DBPtr&lt;Tuple&gt; pt = new Tuple;
<A NAME="61969"></A>
...
<A NAME="61970"></A>
delete pt;
</PRE>
</UL><A NAME="61971"></A>
<P><A NAME="dingp71"></A>
<A NAME="p173"></A>This should not compile. After all, <CODE>pt</CODE> is not a pointer, it's an object, and you can't delete an object. Only pointers can be deleted, <NOBR>right?<SCRIPT>create_link(71);</SCRIPT>
</NOBR></P><A NAME="61975"></A>
<P><A NAME="dingp72"></A>
Right. But remember from <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A> that compilers use implicit type conversions to make function calls succeed whenever they can, and recall from <A HREF="./MI8_FR.HTM#33985" TARGET="_top">Item 8</A> that use of the <CODE>delete</CODE> operator leads to calls to a destructor and to <CODE>operator</CODE> <CODE>delete</CODE>, both of which are functions. Compilers want these function calls to succeed, so in the <CODE>delete</CODE> statement above, they implicitly convert <CODE>pt</CODE> to a <CODE>Tuple*</CODE>, then they delete that. This will almost certainly break your <NOBR>program.<SCRIPT>create_link(72);</SCRIPT>
</NOBR></P><A NAME="61979"></A>
<P><A NAME="dingp73"></A>
If <CODE>pt</CODE> owns the object it points to, that object is now deleted twice, once at the point where <CODE>delete</CODE> is called, a second time when <CODE>pt</CODE>'s destructor is invoked. If <CODE>pt</CODE> doesn't own the object, somebody else does. That somebody may be the person who deleted <CODE>pt</CODE>, in which case all is well. If, however, the owner of the object pointed to by <CODE>pt</CODE> is not the person who deleted <CODE>pt</CODE>, we can expect the rightful owner to delete that object again later. The first and last of these scenarios leads to an object being deleted twice, and deleting an object more than once yields undefined <NOBR>behavior.<SCRIPT>create_link(73);</SCRIPT>
</NOBR></P><A NAME="61980"></A>
<P><A NAME="dingp74"></A>
This bug is especially pernicious because the whole idea behind smart pointers is to make them look and feel as much like dumb pointers as possible. The closer you get to this ideal, the more likely your clients are to forget they are using smart pointers. If they do, who can blame them if they continue to think that in order to avoid resource leaks, they must call <CODE>delete</CODE> if they called <CODE>new</CODE>?<SCRIPT>create_link(74);</SCRIPT>
</P><A NAME="61981"></A>
<P><A NAME="dingp75"></A>
The bottom line is simple: don't provide implicit conversion operators to dumb pointers unless there is a compelling reason to do <NOBR>so.<SCRIPT>create_link(75);</SCRIPT>
</NOBR></P>
<A NAME="61982"></A>
<P><A NAME="dingp76"></A><FONT ID="mhtitle">Smart Pointers and Inheritance-Based Type Conversions</FONT><SCRIPT>create_link(76);</SCRIPT>
</P>

<P><A NAME="dingp77"></A><A NAME="61983"></A>
Suppose we have a public inheritance hierarchy modeling consumer products for storing <NOBR>music:<SCRIPT>create_link(77);</SCRIPT>
</NOBR></P>

<SPAN ID="Image1of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_173A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_173A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_173A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_173A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image1of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_173A5.GIF" BORDER=0></SPAN>

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

<A NAME="61999"></A>
<UL><PRE>class MusicProduct {
public:
  MusicProduct(const string&amp; title);
  virtual void play() const = 0;
  virtual void displayTitle() const = 0;
  ...
};
<A NAME="62002"></A>
<A NAME="p174"></A>class Cassette: public MusicProduct {
public:
  Cassette(const string&amp; title);
  virtual void play() const;
  virtual void displayTitle() const;
  ...
};
<A NAME="62003"></A>
class CD: public MusicProduct {
public:
  CD(const string&amp; title);
  virtual void play() const;
  virtual void displayTitle() const;
  ...
};
</PRE>
</UL><A NAME="62004"></A>
<P><A NAME="dingp78"></A>
Further suppose we have a function that, given a <CODE>MusicProduct</CODE> object, displays the title of the product and then plays <NOBR>it:<SCRIPT>create_link(78);</SCRIPT>
</NOBR></P>
<A NAME="62005"></A>
<UL><PRE>void displayAndPlay(const MusicProduct* pmp, int numTimes)
{
  for (int i = 1; i &lt;= numTimes; ++i) {
    pmp-&gt;displayTitle();
    pmp-&gt;play();
  }
}
</PRE>
</UL><A NAME="62006"></A>
<P><A NAME="dingp79"></A>
Such a function might be used like this:
<A NAME="62007"></A><SCRIPT>create_link(79);</SCRIPT>
</P>
<UL><PRE>Cassette *funMusic = new Cassette("Alapalooza");
CD *nightmareMusic = new CD("Disco Hits of the 70s");
<A NAME="62008"></A>
displayAndPlay(funMusic, 10);
displayAndPlay(nightmareMusic, 0);
</PRE>
</UL><A NAME="62009"></A>
<P><A NAME="dingp80"></A>
There are no surprises here, but look what happens if we replace the dumb pointers with their allegedly smart <NOBR>counterparts:<SCRIPT>create_link(80);</SCRIPT>
</NOBR></P>
<A NAME="62010"></A>
<UL><PRE>void displayAndPlay(const SmartPtr&lt;MusicProduct&gt;&amp; pmp,
                    int numTimes);
<A NAME="62011"></A>
SmartPtr&lt;Cassette&gt; funMusic(new Cassette("Alapalooza"));
SmartPtr&lt;CD&gt; nightmareMusic(new CD("Disco Hits of the 70s"));
<A NAME="62012"></A>

⌨️ 快捷键说明

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