📄 mi29.htm
字号:
void RCPtr<T>::init()
{
if (pointee == 0) { // if the dumb pointer is
return; // null, so is the smart one
}
</PRE>
</UL><A NAME="17888"></A>
<UL><PRE>
if (pointee->isShareable() == false) { // if the value
pointee = new T(*pointee); // isn't shareable,
} // copy it
</PRE>
</UL><A NAME="73816"></A>
<UL><PRE>
pointee->addReference(); // note that there is now a
} // new reference to the value
</PRE>
</UL>
<P><A NAME="dingp82"></A><A NAME="17940"></A>
Moving common code into a separate function like <CODE>init</CODE> is exemplary software engineering, but its luster dims when, as in this case, the function doesn't behave <NOBR>correctly.<SCRIPT>create_link(82);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp83"></A><A NAME="9632"></A>
The problem is this. When <CODE>init</CODE> needs to create a new copy of a value (because the existing copy isn't shareable), it executes the following <NOBR>code:<SCRIPT>create_link(83);</SCRIPT>
</NOBR></P><A NAME="73831"></A>
<UL><PRE>pointee = new T(*pointee);
</PRE>
</UL>
<P><A NAME="dingp84"></A><A NAME="73832"></A>
The type of <CODE>pointee</CODE> is pointer-to-<CODE>T</CODE>, so this statement creates a new <CODE>T</CODE> object and initializes it by calling <CODE>T</CODE>'s copy constructor. In the case of an <CODE>RCPtr</CODE> in the <CODE>String</CODE> class, <CODE>T</CODE> will be <CODE>String::StringValue</CODE>, so the statement above will call <CODE>String::StringValue</CODE>'s copy constructor. We haven't declared a copy constructor for that class, however, so our compilers will generate one for us. The copy constructor so generated will, in accordance with the rules for automatically generated copy constructors in C++, copy only <CODE>StringValue</CODE>'s <CODE>data</CODE> <I>pointer</I>; it will <I>not</I> copy the <CODE>char*</CODE> string <CODE>data</CODE> points to. Such behavior is disastrous in nearly <I>any</I> class (not just reference-counted classes), and that's why you should get into the habit of writing a copy constructor (and an assignment operator) for all your classes that contain pointers (see <a href="../EC/EI11_FR.HTM#2042" TARGET="_top">Item E11</A>).<SCRIPT>create_link(84);</SCRIPT>
</P>
<A NAME="9648"></A>
<P><A NAME="dingp85"></A><A NAME="18074"></A>
<A NAME="p201"></A>The correct behavior of the <CODE>RCPtr<T></CODE> template depends on <CODE>T</CODE> containing a copy constructor that makes a truly independent copy (i.e., a <I>deep copy</I>) of the value represented by <CODE>T</CODE>. We must augment <CODE>StringValue</CODE> with such a constructor before we can use it with the <CODE>RCPtr</CODE> <NOBR>class:<SCRIPT>create_link(85);</SCRIPT>
</NOBR></P><A NAME="18088"></A>
<UL><PRE>class String {
private:
</PRE>
</UL><A NAME="9653"></A>
<UL><PRE> struct StringValue: public RCObject {
StringValue(const StringValue& rhs);
</PRE>
</UL><A NAME="9654"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="9655"></A>
<UL><PRE> };
</PRE>
</UL><A NAME="18101"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="76097"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="18102"></A>
<UL><PRE>String::StringValue::StringValue(const StringValue& rhs)
{
data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
}
</PRE>
</UL>
<P><A NAME="dingp86"></A><A NAME="18052"></A>
The existence of a deep-copying copy constructor is not the only assumption <CODE>RCPtr<T></CODE> makes about <CODE>T</CODE>. It also requires that <CODE>T</CODE> inherit from <CODE>RCObject</CODE>, or at least that <CODE>T</CODE> provide all the functionality that <CODE>RCObject</CODE> does. In view of the fact that <CODE>RCPtr</CODE> objects are designed to point only to reference-counted objects, this is hardly an unreasonable assumption. Nevertheless, the assumption must be <NOBR>documented.<SCRIPT>create_link(86);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp87"></A><A NAME="72713"></A>
A final assumption in <CODE>RCPtr<T></CODE> is that the type of the object pointed to is <CODE>T</CODE>. This seems obvious enough. After all, <CODE>pointee</CODE> is declared to be of type <CODE>T*</CODE>. But <CODE>pointee</CODE> might really point to a class <I>derived</I> from <CODE>T</CODE>. For example, if we had a class <CODE>SpecialStringValue</CODE> that inherited from <CODE>String</CODE>::<CODE>StringValue</CODE>,<SCRIPT>create_link(87);</SCRIPT>
</P><A NAME="72730"></A>
<UL><PRE>class String {
private:
struct StringValue: public RCObject { ... };
</PRE>
</UL><A NAME="72736"></A>
<UL><PRE> struct SpecialStringValue: public StringValue { ... };
</PRE>
</UL><A NAME="72798"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="9656"></A>
<UL><PRE>};
</PRE>
</UL>
<P><A NAME="dingp88"></A><A NAME="72728"></A>
<A NAME="p202"></A>we could end up with a <CODE>String</CODE> containing a <CODE>RCPtr<StringValue></CODE> pointing to a <CODE>SpecialStringValue</CODE> object. In that case, we'd want this part of <CODE>init</CODE>,<SCRIPT>create_link(88);</SCRIPT>
</P><A NAME="72757"></A>
<UL><PRE>
pointee = new T(*pointee); // T is StringValue, but
// pointee really points to
// a SpecialStringValue
</PRE>
</UL>
<P><A NAME="dingp89"></A><A NAME="72767"></A>
to call <CODE>SpecialStringValue</CODE>'s copy constructor, not <CODE>StringValue</CODE>'s. We can arrange for this to happen by using a virtual copy constructor (see <a href="./MI25_FR.HTM#5341" TARGET="_top">Item 25</A>). In the case of our <CODE>String</CODE> class, we don't expect classes to derive from <CODE>StringValue</CODE>, so we'll disregard this <NOBR>issue.<SCRIPT>create_link(89);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp90"></A><A NAME="18359"></A>
With <CODE>RCPtr</CODE>'s constructors out of the way, the rest of the class's functions can be dispatched with considerably greater alacrity. Assignment of an <CODE>RCPtr</CODE> is straightforward, though the need to test whether the newly assigned value is shareable complicates matters slightly. Fortunately, such complications have already been handled by the <CODE>init</CODE> function that was created for <CODE>RCPtr</CODE>'s constructors. We take advantage of that fact by using it again <NOBR>here:<SCRIPT>create_link(90);</SCRIPT>
</NOBR></P><A NAME="18360"></A>
<UL><PRE>template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs)
{
if (pointee != rhs.pointee) { // skip assignments
// where the value
// doesn't change
if (pointee) {
pointee->removeReference(); // remove reference to
} // current value
</PRE>
</UL><A NAME="18361"></A>
<UL><PRE>
pointee = rhs.pointee; // point to new value
init(); // if possible, share it
} // else make own copy
</PRE>
</UL><A NAME="18362"></A>
<UL><PRE> return *this;
}
</PRE>
</UL>
<P><A NAME="dingp91"></A><A NAME="18355"></A>
The destructor is easier. When an <CODE>RCPtr</CODE> is destroyed, it simply removes its reference to the reference-counted <NOBR>object:<SCRIPT>create_link(91);</SCRIPT>
</NOBR></P><A NAME="18157"></A>
<UL><PRE>template<class T>
RCPtr<T>::~RCPtr()
{
if (pointee)pointee->removeReference();
}
</PRE>
</UL>
<P><A NAME="dingp92"></A><A NAME="18331"></A>
If the <CODE>RCPtr</CODE> that just expired was the last reference to the object, that object will be destroyed inside <CODE>RCObject</CODE>'s <CODE>removeReference</CODE> member function. Hence <CODE>RCPtr</CODE> objects never need to worry about destroying the values they point <NOBR>to.<SCRIPT>create_link(92);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp93"></A><A NAME="18304"></A>
<A NAME="p203"></A>Finally, <CODE>RCPtr</CODE>'s pointer-emulating operators are part of the smart pointer boilerplate you can read about in <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A>:<SCRIPT>create_link(93);</SCRIPT>
</P><A NAME="59443"></A>
<UL><PRE>template<class T>
T* RCPtr<T>::operator->() const { return pointee; }
</PRE>
</UL><A NAME="59431"></A>
<UL><PRE>template<class T>
T& RCPtr<T>::operator*() const { return *pointee; }
</PRE>
</UL>
<P><A NAME="dingp94"></A><FONT ID="mhtitle">Putting it All Together</FONT><SCRIPT>create_link(94);</SCRIPT>
</P>
<P><A NAME="dingp95"></A><A NAME="18130"></A>
Enough! <I>Finis</I>! At long last we are in a position to put all the pieces together and build a reference-counted <CODE>String</CODE> class based on the reusable <CODE>RCObject</CODE> and <CODE>RCPtr</CODE> classes. With luck, you haven't forgotten that that was our original <NOBR>goal.<SCRIPT>create_link(95);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp96"></A><A NAME="59644"></A>
Each reference-counted string is implemented via this data <NOBR>structure:<SCRIPT>create_link(96);</SCRIPT>
</NOBR></P>
<SPAN ID="Image9of1" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_203A1.GIF" BORDER=0></SPAN>
<SPAN ID="Image9of2" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_203A2.GIF" BORDER=0></SPAN>
<SPAN ID="Image9of3" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_203A3.GIF" BORDER=0></SPAN>
<SPAN ID="Image9of4" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_203A4.GIF" BORDER=0></SPAN>
<SPAN ID="Image9of5" STYLE="position: absolute; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_203A5.GIF" BORDER=0></SPAN>
<SPAN ID="Image9of6" STYLE="position: relative; z-index:1; visibility: hidden"><IMG SRC="./IMAGES/GRAPHICS/DIAGRAMS/I_203A5.GIF" BORDER=0></SPAN>
<P><A NAME="dingp97"></A><A NAME="59456"></A>
The classes making up this data structure are defined like <NOBR>this:<SCRIPT>create_link(97);</SCRIPT>
</NOBR></P><A NAME="18393"></A>
<UL><PRE>
template<class T> // template class for smart
class RCPtr { // pointers-to-T objects; T
public: // must inherit from RCObject
RCPtr(T* realPtr = 0);
RCPtr(const RCPtr& rhs);
~RCPtr();
<A NAME="18394"></A>
RCPtr& operator=(const RCPtr& rhs);
<A NAME="18398"></A>
T* operator->() const;
T& operator*() const;
<A NAME="18402"></A>
private:
T *pointee;
<A NAME="18403"></A>
void init();
};
<A NAME="57786"></A>
<A NAME="p204"></A>
class RCObject { // base class for reference-
public: // counted objects
void addReference();
void removeRefere
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -