📄 mi29.htm
字号:
<UL><PRE>bool isShared() const;
</PRE>
</UL><A NAME="57773"></A>
<UL><PRE>private:
int refCount;
bool shareable;
};
</PRE>
</UL>
<P><A NAME="dingp62"></A><A NAME="16988"></A>
<CODE>RCObject</CODE>s can be created (as the base class parts of more derived objects) and destroyed; they can have new references added to them and can have current references removed; their shareability status can be queried and can be disabled; and they can report whether they are currently being shared. That's all they offer. As a class encapsulating the notion of being reference-countable, that's really all we have a right to expect them to do. Note the tell-tale virtual destructor, a sure sign this class is designed for use as a base class (see <a href="../EC/EI14_FR.HTM#223029" TARGET="_top">Item E14</A>). Note also how the destructor is a <I>pure</I> virtual function, a sure sign this class is designed to be used <I>only</I> as a base <NOBR>class.<SCRIPT>create_link(62);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp63"></A><A NAME="17040"></A>
The code to implement <CODE>RCObject</CODE> is, if nothing else, <NOBR>brief:<SCRIPT>create_link(63);</SCRIPT>
</NOBR></P><A NAME="17001"></A>
<UL><PRE>RCObject::RCObject()
: refCount(0), shareable(true) {}
<A NAME="17002"></A>
RCObject::RCObject(const RCObject&)
: refCount(0), shareable(true) {}
<A NAME="72582"></A>
>RCObject& RCObject::operator=(const RCObject&)
{ return *this; }
<A NAME="17038"></A>
RCObject::~RCObject() {} // virtual dtors must always
// be implemented, even if
// they are pure virtual
// and do nothing (see also
// <a href="./MI33_FR.HTM" TARGET="_top">Item 33</A> and <a href="../EC/EI14_FR.HTM#223029" TARGET="_top">Item E14</A>)
<A NAME="17044"></A>
void RCObject::addReference() { ++refCount; }
<A NAME="17045"></A>
void RCObject::removeReference()
{ if (--refCount == 0) delete this; }
<A NAME="17977"></A>
void RCObject::markUnshareable()
{ shareable = false; }
<A NAME="17983"></A>
bool RCObject::isShareable() const
{ return shareable; }
<A NAME="18792"></A>
bool RCObject::isShared() const
{ return refCount > 1; }
</PRE>
</UL>
<P><A NAME="dingp64"></A><A NAME="17052"></A>
<A NAME="p196"></A>Curiously, we set <CODE>refCount</CODE> to 0 inside both constructors. This seems counterintuitive. Surely at least the creator of the new <CODE>RCObject</CODE> is referring to it! As it turns out, it simplifies things for the creators of <CODE>RCObject</CODE>s to set <CODE>refCount</CODE> to 1 themselves, so we oblige them here by not getting in their way. We'll get a chance to see the resulting code simplification <NOBR>shortly.<SCRIPT>create_link(64);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp65"></A><A NAME="17657"></A>
Another curious thing is that the copy constructor always sets <CODE>refCount</CODE> to 0, regardless of the value of <CODE>refCount</CODE> for the <CODE>RCObject</CODE> we're copying. That's because we're creating a new object representing a value, and new values are always unshared and referenced only by their creator. Again, the creator is responsible for setting the <CODE>refCount</CODE> to its proper <NOBR>value.<SCRIPT>create_link(65);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp66"></A><A NAME="72587"></A>
The <CODE>RCObject</CODE> assignment operator looks downright subversive: it does <I>nothing</I>. Frankly, it's unlikely this operator will ever be called. <CODE>RCObject</CODE> is a base class for a shared <I>value</I> object, and in a system based on reference counting, such objects are not assigned to one another, objects <I>pointing</I> to them are. In our case, we don't expect <CODE>StringValue</CODE> objects to be assigned to one another, we expect only <CODE>String</CODE> objects to be involved in assignments. In such assignments, no change is made to the value of a <CODE>StringValue</CODE> — only the <CODE>StringValue</CODE> reference count is <NOBR>modified.<SCRIPT>create_link(66);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp67"></A><A NAME="72612"></A>
Nevertheless, it is conceivable that some as-yet-unwritten class might someday inherit from <CODE>RCObject</CODE> and might wish to allow assignment of reference-counted values (see <a href="./MI32_FR.HTM#5373" TARGET="_top">Item 32</A> and <a href="../EC/EI16_FR.HTM#2225" TARGET="_top">Item E16</A>). If so, <CODE>RCObject</CODE>'s assignment operator should do the right thing, and the right thing is to do nothing. To see why, imagine that we wished to allow assignments between <CODE>StringValue</CODE> objects. Given <CODE>StringValue</CODE> objects <CODE>sv1</CODE> and <CODE>sv2</CODE>, what should happen to <CODE>sv1</CODE>'s and <CODE>sv2</CODE>'s reference counts in an <NOBR>assignment?<SCRIPT>create_link(67);</SCRIPT>
</NOBR></P><A NAME="72626"></A>
<UL><PRE>
sv1 = sv2; // how are sv1's and sv2's reference
// counts affected?
</PRE>
</UL>
<P><A NAME="dingp68"></A><A NAME="72627"></A>
Before the assignment, some number of <CODE>String</CODE> objects are pointing to <CODE>sv1</CODE>. That number is unchanged by the assignment, because only <CODE>sv1</CODE>'s <I>value</I> changes. Similarly, some number of <CODE>String</CODE> objects are pointing to <CODE>sv2</CODE> prior to the assignment, and after the assignment, exactly the same <CODE>String</CODE> objects point to <CODE>sv2</CODE>. <CODE>sv2</CODE>'s reference count is also unchanged. When <CODE>RCObjects</CODE> are involved in an assignment, then, the number of objects pointing to those objects is unaffected, hence <CODE>RCObject</CODE>::<CODE>operator=</CODE> should change no reference counts. That's exactly what the implementation above does. Counterintuitive? Perhaps, but it's still <NOBR>correct.<SCRIPT>create_link(68);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp69"></A><A NAME="17055"></A>
The code for <CODE>RCObject::removeReference</CODE> is responsible not only for decrementing the object's <CODE>refCount</CODE>, but also for destroying the object <A NAME="p197"></A>if the new value of <CODE>refCount</CODE> is 0. It accomplishes this latter task by <CODE>delete</CODE>ing <CODE>this</CODE>, which, as <a href="./MI27_FR.HTM#22627" TARGET="_top">Item 27</A> explains, is safe only if we know that <CODE>*this</CODE> is a heap object. For this class to be successful, we must engineer things so that <CODE>RCObject</CODE>s can be created only on the heap. General approaches to achieving that end are discussed in <a href="./MI27_FR.HTM#22627" TARGET="_top">Item 27</A>, but the specific measures we'll employ in this case are described at the conclusion of this <NOBR>Item.<SCRIPT>create_link(69);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp70"></A><A NAME="17200"></A>
To take advantage of our new reference-counting base class, we modify <CODE>StringValue</CODE> to inherit its reference counting capabilities from <CODE>RCObject</CODE>:<SCRIPT>create_link(70);</SCRIPT>
</P><A NAME="17239"></A>
<UL><PRE>class String {
private:
struct StringValue: public RCObject {
char *data;
<A NAME="17240"></A>
StringValue(const char *initValue);
~StringValue();
<A NAME="17241"></A>
};
<A NAME="17242"></A>
...
<A NAME="17243"></A>
};
<A NAME="17244"></A>
String::StringValue::StringValue(const char *initValue)
{
data = new char[strlen(initValue) + 1];
strcpy(data, initValue);
}
<A NAME="17245"></A>
String::StringValue::~StringValue()
{
delete [] data;
}
</PRE>
</UL>
<P><A NAME="dingp71"></A><A NAME="17237"></A>
This version of <CODE>StringValue</CODE> is almost identical to the one we saw earlier. The only thing that's changed is that <CODE>StringValue</CODE>'s member functions no longer manipulate the <CODE>refCount</CODE> field. <CODE>RCObject</CODE> now handles what they used to <NOBR>do.<SCRIPT>create_link(71);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp72"></A><A NAME="17283"></A>
Don't feel bad if you blanched at the sight of a nested class (<CODE>StringValue</CODE>) inheriting from a class (<CODE>RCObject</CODE>) that's unrelated to the nesting class (<CODE>String</CODE>). It looks weird to everybody at first, but it's perfectly kosher. A nested class is just as much a class as any other, so it has the freedom to inherit from whatever other classes it likes. In time, you won't think twice about such inheritance <NOBR>relationships.<SCRIPT>create_link(72);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp73"></A><FONT ID="mhtitle"><A NAME="p198"></A>Automating Reference Count Manipulations</FONT><SCRIPT>create_link(73);</SCRIPT>
</P>
<P><A NAME="dingp74"></A><A NAME="17236"></A>
The <CODE>RCObject</CODE> class gives us a place to store a reference count, and it gives us member functions through which that reference count can be manipulated, but the <I>calls</I> to those functions must still be manually inserted in other classes. It is still up to the <CODE>String</CODE> copy constructor and the <CODE>String</CODE> assignment operator to call <CODE>addReference</CODE> and <CODE>removeReference</CODE> on <CODE>StringValue</CODE> objects. This is clumsy. We'd like to move those calls out into a reusable class, too, thus freeing authors of classes like <CODE>String</CODE> from worrying about <I>any</I> of the details of reference counting. Can it be done? Isn't C++ supposed to support <NOBR>reuse?<SCRIPT>create_link(74);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp75"></A><A NAME="17340"></A>
It can, and it does. There's no easy way to arrange things so that <i>all</i> reference-counting considerations can be moved out of application classes, but there is a way to eliminate <I>most</I> of them for most classes. (In some application classes, you <I>can</I> eliminate all reference-counting code, but our <CODE>String</CODE> class, alas, isn't one of them. One member function spoils the party, and I suspect you won't be too surprised to hear it's our old nemesis, the non-<CODE>const</CODE> version of <CODE>operator[]</CODE>. Take heart, however; we'll tame that miscreant in the <NOBR>end.)<SCRIPT>create_link(75);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp76"></A><A NAME="17342"></A>
Notice that each <CODE>String</CODE> object contains a pointer to the <CODE>StringValue</CODE> object representing that <CODE>String</CODE>'s <NOBR>value:<SCRIPT>create_link(76);</SCRIPT>
</NOBR></P><A NAME="17345"></A>
<UL><PRE>class String {
private:
struct StringValue: public RCObject { ... };
</PRE>
</UL><A NAME="17369"></A>
<UL><PRE> StringValue *value; // value of this String
</PRE>
</UL><A NAME="17348"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="17376"></A>
<UL><PRE>};
</PRE>
</UL>
<P><A NAME="dingp77"></A><A NAME="17380"></A>
We have to manipulate the <CODE>refCount</CODE> field of the <CODE>StringValue</CODE> object anytime anything interesting happens to one of the pointers pointing to it. "Interesting happenings" include copying a pointer, reassigning one, and destroying one. If we could somehow make the <I>pointer itself</I> detect these happenings and automatically perform the necessary manipulations of the <CODE>refCount</CODE> field, we'd be home free. Unfortunately, pointers are rather dense creatures, and the chances of them detecting anything, much less automatically reacting to things they detect, are pretty slim. Fortunately, there's a way to smarten them up: replace them with objects that <I>act like</I> pointers, but that do <NOBR>more.<SCRIPT>create_link(77);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp78"></A><A NAME="17408"></A>
Such objects are called <I>smart pointers</I>, and you can read about them in more detail than you probably care to in <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A>. For our purposes here, it's enough to know that smart pointer objects support the member selection (<CODE>-></CODE>) and dereferencing (<CODE>*</CODE>) operations, just like real pointers (which, in this context, are generally referred to as <I>dumb pointers</I>), <A NAME="p199"></A>and, like dumb pointers, they are strongly typed: you can't make a smart pointer-to-T point to an object that isn't of type <NOBR>T.<SCRIPT>create_link(78);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp79"></A><A NAME="17436"></A>
Here's a template for objects that act as smart pointers to reference-counted <NOBR>objects:<SCRIPT>create_link(79);</SCRIPT>
</NOBR></P><A NAME="17439"></A>
<UL><PRE>// template class for smart pointers-to-T objects. T must
// support the RCObject interface, typically by inheriting
// from RCObject
template<class T>
class RCPtr {
public:
RCPtr(T* realPtr = 0);
RCPtr(const RCPtr& rhs);
~RCPtr();
</PRE>
</UL><A NAME="17456"></A>
<UL><PRE> RCPtr& operator=(const RCPtr& rhs);
</PRE>
</UL><A NAME="17440"></A>
<UL><PRE>
T* operator->() const; // see <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A>
T& operator*() const; // see <a href="./MI28_FR.HTM#61766" TARGET="_top">Item 28</A>
</PRE>
</UL><A NAME="17442"></A>
<UL><PRE>private:
T *pointee; // dumb pointer this
// object is emulating
</PRE>
</UL><A NAME="17905"></A>
<UL><PRE>
void init(); // common initialization
}; // code
</PRE>
</UL>
<P><A NAME="dingp80"></A><A NAME="17381"></A>
This template gives smart pointer objects control over what happens during their construction, assignment, and destruction. When such events occur, these objects can automatically perform the appropriate manipulations of the <CODE>refCount</CODE> field in the objects to which they <NOBR>point.<SCRIPT>create_link(80);</SCRIPT>
</NOBR></P>
<P><A NAME="dingp81"></A><A NAME="17687"></A>
For example, when an <CODE>RCPtr</CODE> is created, the object it points to needs to have its reference count increased. There's no need to burden application developers with the requirement to tend to this irksome detail manually, because <CODE>RCPtr</CODE> constructors can handle it themselves. The code in the two constructors is all but identical — only the member initialization lists differ — so rather than write it twice, we put it in a private member function called <CODE>init</CODE> and have both constructors call <NOBR>that:<SCRIPT>create_link(81);</SCRIPT>
</NOBR></P><A NAME="17920"></A>
<UL><PRE>template<class T>
RCPtr<T>::RCPtr(T* realPtr): pointee(realPtr)
{
init();
}
</PRE>
</UL><A NAME="17921"></A>
<UL><PRE>template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs): pointee(rhs.pointee)
{
init();
}
</PRE>
</UL><A NAME="86769"></A>
<UL><PRE><A NAME="p200"></A>template<class T>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -