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

📄 mi28.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
displayAndPlay(funMusic, 10);         // error!
displayAndPlay(nightmareMusic, 0);    // error!
</PRE>
</UL><A NAME="84619"></A>
<P><A NAME="dingp81"></A>
If smart pointers are so brainy, why won't these <NOBR>compile?<SCRIPT>create_link(81);</SCRIPT>
</NOBR></P><A NAME="84620"></A>
<P><A NAME="dingp82"></A>
They won't compile because there is no conversion from a <CODE>SmartPtr&lt;CD&gt;</CODE> or a <CODE>SmartPtr&lt;Cassette&gt;</CODE> to a <CODE>SmartPtr&lt;MusicProduct&gt;</CODE>. As far as compilers are concerned, these are three separate classes &#151; they have no relationship to one another. Why should compilers think otherwise? After all, it's not like <CODE>SmartPtr&lt;CD&gt;</CODE> or <CODE>SmartPtr&lt;Cassette&gt;</CODE> inherits from<CODE> SmartPtr&lt;MusicProduct&gt;</CODE>. With no inheritance relationship be<A NAME="p175"></A>tween these classes, we can hardly expect compilers to run around converting objects of one type to objects of other <NOBR>types.<SCRIPT>create_link(82);</SCRIPT>
</NOBR></P><A NAME="62015"></A>
<P><A NAME="dingp83"></A>
Fortunately, there is a way to get around this limitation, and the idea (if not the practice) is simple: give each smart pointer class an implicit type conversion operator (see <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>) for each smart pointer class to which it should be implicitly convertible. For example, in the music hierarchy, you'd add an <CODE>operator</CODE> <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> to the smart pointer classes for <CODE>Cassette</CODE> and <CODE>CD</CODE>:<SCRIPT>create_link(83);</SCRIPT>
</P>
<A NAME="62019"></A>
<UL><PRE>class SmartPtr&lt;Cassette&gt; {
public:
  operator SmartPtr&lt;MusicProduct&gt;()
  { return SmartPtr&lt;MusicProduct&gt;(pointee); }
<A NAME="62020"></A>
  ...
<A NAME="62021"></A>
private:
  Cassette *pointee;
};
<A NAME="62022"></A>
class SmartPtr&lt;CD&gt; {
public:
  operator SmartPtr&lt;MusicProduct&gt;()
  { return SmartPtr&lt;MusicProduct&gt;(pointee); }
<A NAME="62023"></A>
  ...
<A NAME="62024"></A>
private:
  CD *pointee;
};
</PRE>
</UL><A NAME="62025"></A>
<P><A NAME="dingp84"></A>
The drawbacks to this approach are twofold. First, you must manually specialize the <CODE>SmartPtr</CODE> class instantiations so you can add the necessary implicit type conversion operators, but that pretty much defeats the purpose of templates. Second, you may have to add many such conversion operators, because your pointed-to object may be deep in an inheritance hierarchy, and you must provide a conversion operator for <I>each</I> base class from which that object directly or indirectly inherits. (If you think you can get around this by providing only an implicit type conversion operator for each direct base class, think again. Because compilers are prohibited from employing more than one user-defined type conversion function at a time, they can't convert a smart pointer-to-<CODE>T</CODE> to a smart pointer-to-indirect-base-class-of-<CODE>T</CODE> unless they can do it in a single <NOBR>step.)<SCRIPT>create_link(84);</SCRIPT>
</NOBR></P><A NAME="62029"></A>
<P><A NAME="dingp85"></A>
It would be quite the time-saver if you could somehow get compilers to write all these implicit type conversion functions for you. Thanks to a recent language extension, you can. The extension in question is the ability to declare (nonvirtual) <I>member function templates</I> (usually just <A NAME="p176"></A>called <I>member templates</I>), and you use it to generate smart pointer conversion functions like <NOBR>this:<SCRIPT>create_link(85);</SCRIPT>
</NOBR></P>
<A NAME="62031"></A>
<UL><PRE>template&lt;class T&gt;                    // template class for smart
class SmartPtr {                     // pointers-to-T objects
public:
  SmartPtr(T* realPtr = 0);
<A NAME="62032"></A>
  T* operator-&gt;() const;
  T&amp; operator*() const;
<A NAME="62033"></A>
  template&lt;class newType&gt;             // template function for
  operator SmartPtr&lt;newType&gt;()        // implicit conversion ops.
  {
    return SmartPtr&lt;newType&gt;(pointee);
  }
<A NAME="62034"></A>
  ...
};
</PRE>
</UL><A NAME="62036"></A>
<P><A NAME="dingp86"></A>
Now hold on to your headlights, this isn't magic &#151; but it's close. It works as follows. (I'll give a specific example in a moment, so don't despair if the remainder of this paragraph reads like so much gobbledygook. After you've seen the example, it'll make more sense, I promise.) Suppose a compiler has a smart pointer-to-<CODE>T</CODE> object, and it's faced with the need to convert that object into a smart pointer-to-base-class-of-<CODE>T</CODE>. The compiler checks the class definition for <CODE>SmartPtr&lt;T&gt;</CODE> to see if the requisite conversion operator is declared, but it is not. (It can't be: no conversion operators are declared in the template above.) The compiler then checks to see if there's a member function template it can instantiate that would let it perform the conversion it's looking for. It finds such a template (the one taking the formal type parameter <CODE>newType</CODE>), so it instantiates the template with <CODE>newType</CODE> bound to the base class of <CODE>T</CODE> that's the target of the conversion. At that point, the only question is whether the code for the instantiated member function will compile. In order for it to compile, it must be legal to pass the (dumb) pointer <CODE>pointee</CODE> to the constructor for the smart pointer-to-base-of-<CODE>T</CODE>. <CODE>pointee</CODE> is of type <CODE>T</CODE>, so it is certainly legal to convert it into a pointer to its (public or protected) base classes. Hence, the code for the type conversion operator will compile, and the implicit conversion from smart pointer-to-<CODE>T</CODE> to smart pointer-to-base-of-<CODE>T</CODE> will <NOBR>succeed.<SCRIPT>create_link(86);</SCRIPT>
</NOBR></P><A NAME="62037"></A>
<P><A NAME="dingp87"></A>
An example will help. Let us return to the music hierarchy of CDs, cassettes, and music products. We saw earlier that the following code wouldn't compile, because there was no way for compilers to convert the smart pointers to CDs or cassettes into smart pointers to music <NOBR>products:<SCRIPT>create_link(87);</SCRIPT>
</NOBR></P>
<A NAME="62038"></A>
<UL><PRE><A NAME="p177"></A>void displayAndPlay(const SmartPtr&lt;MusicProduct&gt;&amp; pmp,
                    int howMany);
<A NAME="62039"></A>
SmartPtr&lt;Cassette&gt; funMusic(new Cassette("Alapalooza"));
SmartPtr&lt;CD&gt; nightmareMusic(new CD("Disco Hits of the 70s"));
<A NAME="62040"></A>
displayAndPlay(funMusic, 10);         // used to be an error
displayAndPlay(nightmareMusic, 0);    // used to be an error
</PRE>
</UL><A NAME="62041"></A>
<P><A NAME="dingp88"></A>
With the revised smart pointer class containing the member function template for implicit type conversion operators, this code will succeed. To see why, look at this <NOBR>call:<SCRIPT>create_link(88);</SCRIPT>
</NOBR></P>
<A NAME="62042"></A>
<UL><PRE>displayAndPlay(funMusic, 10);
</PRE>
</UL><A NAME="62043"></A>
<P><A NAME="dingp89"></A>
The object <CODE>funMusic</CODE> is of type <CODE>SmartPtr&lt;Cassette&gt;</CODE>. The function <CODE>displayAndPlay</CODE> expects a <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> object. Compilers detect the type mismatch and seek a way to convert <CODE>funMusic</CODE> into a <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> object. They look for a single-argument constructor (see <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>) in the <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> class that takes a <CODE>SmartPtr&lt;Cassette&gt;</CODE>, but they find none. They look for an implicit type conversion operator in the <CODE>SmartPtr&lt;Cassette&gt;</CODE> class that yields a <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> class, but that search also fails. They then look for a member function template they can instantiate to yield one of these functions. They discover that the template inside <CODE>SmartPtr&lt;Cassette&gt;</CODE>, when instantiated with <CODE>newType</CODE> bound to <CODE>MusicProduct</CODE>, generates the necessary function. They instantiate the function, yielding the following <NOBR>code:<SCRIPT>create_link(89);</SCRIPT>
</NOBR></P>
<A NAME="62047"></A>
<UL><PRE>SmartPtr&lt;Cassette&gt;::  operator SmartPtr&lt;MusicProduct&gt;()
{
  return SmartPtr&lt;MusicProduct&gt;(pointee);
}
</PRE>
</UL><A NAME="62048"></A>
<P><A NAME="dingp90"></A>
Will this compile? For all intents and purposes, nothing is happening here except the calling of the <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> constructor with <CODE>pointee</CODE> as its argument, so the real question is whether one can construct a <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> object with a <CODE>Cassette*</CODE> pointer. The <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> constructor expects a <CODE>MusicProduct*</CODE> pointer, but now we're on the familiar ground of conversions between dumb pointer types, and it's clear that <CODE>Cassette*</CODE> can be passed in where a <CODE>MusicProduct*</CODE> is expected. The construction of the <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> is therefore successful, and the conversion of the <CODE>SmartPtr&lt;Cassette&gt;</CODE> to <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> is equally successful. <I>Voil&agrave;!</I> Implicit conversion of smart pointer types. What could be <NOBR>simpler?<SCRIPT>create_link(90);</SCRIPT>
</NOBR></P><A NAME="62049"></A>
<P><A NAME="dingp91"></A>
Furthermore, what could be more powerful? Don't be misled by this example into assuming that this works only for pointer conversions up an inheritance hierarchy. The method shown succeeds for <I>any</I> legal <A NAME="p178"></A>implicit conversion between pointer types. If you've got a dumb pointer type <CODE>T1*</CODE> and another dumb pointer type <CODE>T2*</CODE>, you can implicitly convert a smart pointer-to-<CODE>T1</CODE> to a smart pointer-to-<CODE>T2</CODE> if and only if you can implicitly convert a <CODE>T1*</CODE> to a <CODE>T2*</CODE>.<SCRIPT>create_link(91);</SCRIPT>
</P><A NAME="62050"></A>
<P><A NAME="dingp92"></A>
This technique gives you exactly the behavior you want &#151; almost. Suppose we augment our <CODE>MusicProduct</CODE> hierarchy with a new class, <CODE>CasSingle</CODE>, for representing cassette singles. The revised hierarchy looks like <NOBR>this:<SCRIPT>create_link(92);</SCRIPT>
</NOBR></P>

<SPAN ID="Image2of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_178A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_178A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_178A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_178A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image2of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_178A5.GIF" BORDER=0></SPAN>

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

<A NAME="62070"></A>
<P><A NAME="dingp93"></A>
Now consider this <NOBR>code:<SCRIPT>create_link(93);</SCRIPT>
</NOBR></P>
<A NAME="62071"></A>
<UL><PRE>template&lt;class T&gt;                    // as above, including member tem-
class SmartPtr { ... };              // plate for conversion operators
<A NAME="62072"></A>
void displayAndPlay(const SmartPtr&lt;MusicProduct&gt;&amp; pmp,
                    int howMany);
<A NAME="62073"></A>
void displayAndPlay(const SmartPtr&lt;Cassette&gt;&amp; pc,
                    int howMany);
<A NAME="62074"></A>
SmartPtr&lt;CasSingle&gt; dumbMusic(new CasSingle("Achy Breaky Heart"));
<A NAME="62075"></A>
displayAndPlay(dumbMusic, 1);        // error!
</PRE>
</UL><A NAME="62076"></A>
<P><A NAME="dingp94"></A>
In this example, <CODE>displayAndPlay</CODE> is overloaded, with one function taking a <CODE>SmartPtr&lt;MusicProduct&gt;</CODE> object and the other taking a <CODE>SmartPtr&lt;Cassette&gt;</CODE> object. When we invoke <CODE>displayAndPlay</CODE> with a <CODE>SmartPtr&lt;CasSingle&gt;</CODE>, we expect the <CODE>SmartPtr&lt;Cassette&gt;</CODE> function to be chosen, because <CODE>CasSingle</CODE> inherits directly from <CODE>Cassette</CODE> and only indirectly from <CODE>MusicProduct</CODE>. Certainly that's how it would work with dumb pointers. Alas, our smart pointers aren't that smart. They employ member functions as conversion operators, and as far as C++ compilers are concerned, all calls to conversion functions are equally good. As a result, the call to <CODE>displayAndPlay</CODE> is ambiguous, because the conversion from <CODE>SmartPtr&lt;CasSingle&gt;</CODE> to <A NAME="p179"></A><CODE>SmartPtr&lt;Cassette&gt;</CODE> is no better than the conversion to <CODE>SmartPtr&lt;MusicProduct&gt;</CODE>.<SCRIPT>create_link(94);</SCRIPT>
</P><A NAME="62077"></A>
<P><A NAME="dingp95"></A>
Implementing smart pointer conversions through member templates has two additional drawbacks. First, support for member templates is rare, so this technique is currently anything but portable. In the future, that will change, but nobody knows just how far in the future that will be. Second, the mechanics of why this works are far from transparent, relying as they do on a detailed understanding of argument-matching rules for function calls, implicit type conversion functions, implicit instantiation of template functions, and the existence of member function templates. Pity the poor programmer who has never seen this trick before and is then asked to maintain or enhance code that relies on it. The technique is clever, that's for sure, but too much cleverness can be a dangerous <NOBR>thing.<SCRIPT>create_link(95);</SCRIPT>
</NOBR></P><A NAME="80624"></A>
<P><A NAME="dingp96"></A>
Let's stop beating around the bush. What we really want to know is how we can make smart pointer classes behave just like dumb pointers for purposes of inheritance-based type conversions. The answer is simple: we can't. As Daniel Edelson has noted, smart pointers are smart, but they're not pointers. The best we can do is to use member templates to generate conversion functions, then use casts (see <a href="./MI2_FR.HTM#77216" TARGET="_top">Item 2</A>) in those cases where ambiguity results. This isn't a perfect state of affairs, but it's pretty good, and having to cast away ambiguity in a few cases is a small price to pay for the sophisticated functionality smart pointers can <NOBR>provide.<SCRIPT>create_link(96);</SCRIPT>
</NOBR></P>

<P><A NAME="dingp97"></A><FONT ID="mhtitle">Smart Pointers and const</FONT><SCRIPT>create_link(97);</SCRIPT>
</P>

<P><A NAME="dingp98"></A><A NAME="62080"></A>Recall that for dumb pointers, <CODE>const</CODE> can refer to the thing pointed to, to the pointer itself, or both (see <A HREF="../EC/EI21_FR.HTM#6003" TARGET="_top">Item E21</A>):<SCRIPT>create_link(98);</SCRIPT>
</P>
<A NAME="75930"></A>
<UL><PRE>CD goodCD("Flood");
<A NAME="62081"></A>
const CD *p;                         // p is a non-const pointer
                                     // to a const CD object
<A NAME="62083"></A>
CD * const p = &amp;goodCD;              // p is a const pointer to
                                     // a non-const CD object;
                                     // because p is const, it
                                     // must be initialized
<A NAME="62084"></A>
const CD * const p = &amp;goodCD;        // p is a const pointer to
                                     // a const CD object
</PRE>
</UL><A NAME="62085"></A>
<P><A NAME="dingp99"></A>
Naturally, we'd like to have the same flexibility with smart pointers. Unfortunately, there's only one place to put the <CODE>const</CODE>, and there it applies to the pointer, not to the object pointed <NOBR>to:<SCRIPT>create_link(99);</SCRIPT>
</NOBR></P>
<A NAME="62086"></A>
<UL><PRE>const SmartPtr&lt;CD&gt; p =                   // p is a const smart ptr
  &amp;goodCD;                               // to a non-const CD object
</PRE>
</UL><A NAME="62087"></A>
<P><A NAME="dingp100"></A>
<A NAME="p180"></A>This seems simple enough to remedy &#151; just create a smart pointer to a <CODE><I>const</I></CODE> <CODE>CD</CODE>:<SCRIPT>create_link(100);</SCRIPT>
</P>
<A NAME="62088"></A>
<UL><PRE>SmartPtr&lt;const CD&gt; p =                   // p is a non-const smart ptr
  &amp;goodCD;                               // to a const CD object
</PRE>
</UL><A NAME="62089"></A>
<P><A NAME="dingp101"></A>
Now we can create the four combinations of <CODE>const</CODE> and non-<CODE>const</CODE> objects and pointers we <NOBR>seek:<SCRIPT>create_link(101);</SCRIPT>
</NOBR></P>
<A NAME="62090"></A>
<UL><PRE>SmartPtr&lt;CD&gt; p;                          // non-const object,
                   

⌨️ 快捷键说明

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