📄 mi28.htm
字号:
T* SmartPtr<T>::operator->() const
{
<I>perform "smart pointer" processing;</I>
<A NAME="61883"></A>
return pointee;
}
</PRE>
</UL><A NAME="61886"></A>
<P><A NAME="dingp41"></A>This will work fine. Because this function returns a pointer, virtual function calls via <CODE>operator-></CODE> will behave the way they're supposed <NOBR>to.<SCRIPT>create_link(41);</SCRIPT>
</NOBR></P>
<A NAME="80567"></A>
<P><A NAME="dingp42"></A><A NAME="p168"></A>For many applications, this is all you need to know about smart pointers. The reference-counting code of <A HREF="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>, for example, draws on no more functionality than we've discussed here. If you want to push your smart pointers further, however, you must know more about dumb pointer behavior and how smart pointers can and cannot emulate it. If your motto is "Most people stop at the Z — but not me!", the material that follows is for <NOBR>you.<SCRIPT>create_link(42);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp43"></A><FONT ID="mhtitle">Testing Smart Pointers for Nullness</FONT><SCRIPT>create_link(43);</SCRIPT>
</P>
<A NAME="61888"></A>
<P><A NAME="dingp44"></A>With the functions we have discussed so far, we can create, destroy, copy, assign, and dereference smart pointers. One of the things we cannot do, however, is find out if a smart pointer is <NOBR>null:<SCRIPT>create_link(44);</SCRIPT>
</NOBR></P>
<A NAME="61889"></A>
<UL><PRE>SmartPtr<TreeNode> ptn;
<A NAME="61890"></A>
...
<A NAME="61891"></A>
if (ptn == 0) ... // error!
<A NAME="61892"></A>
if (ptn) ... // error!
<A NAME="61893"></A>
if (!ptn) ... // error!
</PRE>
</UL><A NAME="61894"></A>
<P><A NAME="dingp45"></A>
This is a serious <NOBR>limitation.<SCRIPT>create_link(45);</SCRIPT>
</NOBR></P><A NAME="80582"></A>
<P><A NAME="dingp46"></A>
It would be easy to add an <CODE>isNull</CODE> member function to our smart pointer classes, but that wouldn't address the problem that smart pointers don't act like dumb pointers when testing for nullness. A different approach is to provide an implicit conversion operator that allows the tests above to compile. The conversion traditionally employed for this purpose is to <CODE>void*</CODE>:<SCRIPT>create_link(46);</SCRIPT>
</P>
<A NAME="61898"></A>
<UL><PRE>template<class T>
class SmartPtr {
public:
...
operator void*(); // returns 0 if the smart
... // ptr is null, nonzero
}; // otherwise
<A NAME="61899"></A>
SmartPtr<TreeNode> ptn;
<A NAME="61900"></A>
...
<A NAME="61901"></A>
if (ptn == 0) ... // now fine
<A NAME="61902"></A>
if (ptn) ... // also fine
<A NAME="61903"></A>
if (!ptn) ... // fine
</PRE>
</UL><A NAME="61904"></A>
<P><A NAME="dingp47"></A>
This is similar to a conversion provided by the iostream classes, and it explains why it's possible to write code like <NOBR>this:<SCRIPT>create_link(47);</SCRIPT>
</NOBR></P>
<A NAME="61905"></A>
<UL><PRE>ifstream inputFile("datafile.dat");
<A NAME="61906"></A>
<A NAME="p169"></A>if (inputFile) ... // test to see if inputFile
// was successfully
// opened
</PRE>
</UL><A NAME="61907"></A>
<P><A NAME="dingp48"></A>
Like all type conversion functions, this one has the drawback of letting function calls succeed that most programmers would expect to fail (see <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>). In particular, it allows comparisons of smart pointers of completely different <NOBR>types:<SCRIPT>create_link(48);</SCRIPT>
</NOBR></P>
<A NAME="61911"></A>
<UL><PRE>SmartPtr<Apple> pa;
SmartPtr<Orange> po;
<A NAME="61912"></A>
...
<A NAME="61913"></A>
if (pa == po) ... // this compiles!
</PRE>
</UL><A NAME="61914"></A>
<P><A NAME="dingp49"></A>
Even if there is no <CODE>operator==</CODE> taking a <CODE>SmartPtr<Apple></CODE> and a <CODE>SmartPtr<Orange></CODE>, this compiles, because both smart pointers can be implicitly converted into <CODE>void*</CODE> pointers, and there is a built-in comparison function for built-in pointers. This kind of behavior makes implicit conversion functions dangerous. (Again, see <A HREF="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>, and keep seeing it over and over until you can see it in the <NOBR>dark.)<SCRIPT>create_link(49);</SCRIPT>
</NOBR></P><A NAME="61918"></A>
<P><A NAME="dingp50"></A>
There are variations on the conversion-to-<CODE>void*</CODE> motif. Some designers advocate conversion to <CODE>const</CODE> <CODE>void*</CODE>, others embrace conversion to <CODE>bool</CODE>. Neither of these variations eliminates the problem of allowing mixed-type <NOBR>comparisons.<SCRIPT>create_link(50);</SCRIPT>
</NOBR></P><A NAME="61919"></A>
<P><A NAME="dingp51"></A>
There is a middle ground that allows you to offer a reasonable syntactic form for testing for nullness while minimizing the chances of accidentally comparing smart pointers of different types. It is to overload <CODE>operator!</CODE> for your smart pointer classes so that <CODE>operator!</CODE> returns <CODE>true</CODE> if and only if the smart pointer on which it's invoked is <NOBR>null:<SCRIPT>create_link(51);</SCRIPT>
</NOBR></P>
<A NAME="61920"></A>
<UL><PRE>template<class T>
class SmartPtr {
public:
...
bool operator!() const; // returns true if and only
... // if the smart ptr is null
<A NAME="7578"></A>
};
</PRE>
</UL><A NAME="61921"></A>
<P><A NAME="dingp52"></A>
This lets your clients program like <NOBR>this,<SCRIPT>create_link(52);</SCRIPT>
</NOBR></P>
<A NAME="61922"></A>
<UL><PRE><A NAME="p170"></A>SmartPtr<TreeNode> ptn;
<A NAME="61923"></A>
...
<A NAME="61924"></A>
if (!ptn) { // fine
... // ptn is null
}
else {
... // ptn is not null
}
</PRE>
</UL><A NAME="61925"></A>
<A NAME="dingp53"></A>but not like this:<SCRIPT>create_link(53);</SCRIPT>
<A NAME="61926"></A>
<UL><PRE>if (ptn == 0) ... // still an error
<A NAME="61927"></A>
if (ptn) ... // also an error
</PRE>
</UL><A NAME="61928"></A>
<P><A NAME="dingp54"></A>
The only risk for mixed-type comparisons is statements such as <NOBR>these:<SCRIPT>create_link(54);</SCRIPT>
</NOBR></P>
<A NAME="61929"></A>
<UL><PRE>SmartPtr<Apple> pa;
SmartPtr<Orange> po;
<A NAME="61930"></A>
...
<A NAME="61931"></A>
if (!pa == !po) ... // alas, this compiles
</PRE>
</UL><A NAME="61932"></A>
<P><A NAME="dingp55"></A>
Fortunately, programmers don't write code like this very often. Interestingly, iostream library implementations provide an <CODE>operator!</CODE> in addition to the implicit conversion to <CODE>void*</CODE>, but these two functions typically test for slightly different stream states. (In the C++ library standard (see <a href="../EC/EI49_FR.HTM#8392" TARGET="_top">Item E49</A> and <a href="./MI35_FR.HTM#5473" TARGET="_top">Item 35</A>), the implicit conversion to <CODE>void*</CODE> has been replaced by an implicit conversion to <CODE>bool</CODE>, and <CODE>operator</CODE> <CODE>bool</CODE> always returns the negation of <CODE>operator!</CODE>.)<SCRIPT>create_link(55);</SCRIPT>
</P>
<P><A NAME="dingp56"></A><FONT ID="mhtitle">Converting Smart Pointers to Dumb Pointers</FONT><SCRIPT>create_link(56);</SCRIPT>
</P>
<P><A NAME="dingp57"></A><A NAME="61937"></A>Sometimes you'd like to add smart pointers to an application or library that already uses dumb pointers. For example, your distributed database system may not originally have been distributed, so you may have some old library functions that aren't designed to use smart <NOBR>pointers:<SCRIPT>create_link(57);</SCRIPT>
</NOBR></P>
<A NAME="61938"></A>
<UL><PRE>class Tuple { ... }; // as before
<A NAME="61939"></A>
void normalize(Tuple *pt); // put *pt into canonical
// form; note use of dumb
// pointer
</PRE>
</UL><A NAME="61940"></A>
<P><A NAME="dingp58"></A>
Consider what will happen if you try to call <CODE>normalize</CODE> with a smart pointer-to-<CODE>Tuple</CODE>:<SCRIPT>create_link(58);</SCRIPT>
</P>
<A NAME="61941"></A>
<UL><PRE>DBPtr<Tuple> pt;
<A NAME="61942"></A>
...
<A NAME="61943"></A>
normalize(pt); // error!
</PRE>
</UL><A NAME="61944"></A>
<P><A NAME="dingp59"></A>
<A NAME="p171"></A>The call will fail to compile, because there is no way to convert a <CODE>DBPtr<Tuple></CODE> to a <CODE>Tuple*</CODE>. You can make it work by doing <NOBR>this,<SCRIPT>create_link(59);</SCRIPT>
</NOBR></P>
<A NAME="61945"></A>
<UL><PRE>
normalize(&*pt); // gross, but legal
</PRE>
</UL>
<P><A NAME="dingp60"></A><A NAME="61946"></A>
but I hope you'll agree this is <NOBR>repugnant.<SCRIPT>create_link(60);</SCRIPT>
</NOBR></P><A NAME="61947"></A>
<P><A NAME="dingp61"></A>
The call can be made to succeed by adding to the smart pointer-to-T template an implicit conversion operator to a dumb <NOBR>pointer-to-T:<SCRIPT>create_link(61);</SCRIPT>
</NOBR></P><A NAME="61948"></A>
<UL><PRE>
template<class T> // as before
class DBPtr {
public:
...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -