📄 ei17.htm
字号:
<A NAME="2288"></A>
<P><A NAME="dingp12"></A>
<A NAME="p74"></A>By now you know that the solution to the dilemma is to check for an assignment to self and to return immediately if such an assignment is detected. Unfortunately, it's easier to talk about such a check than it is to write it, because you are immediately forced to figure out what it means for two objects to be "the <NOBR>same."<SCRIPT>create_link(12);</SCRIPT>
</NOBR></P>
<A NAME="2289"></A>
<P><A NAME="dingp13"></A>
The topic you confront is technically known as that of <I>object identity</I>, and it's a well-known topic in object-oriented circles. This book is no place for a discourse on object identity, but it is worthwhile to mention the two basic approaches to the <NOBR>problem.<SCRIPT>create_link(13);</SCRIPT>
</NOBR></P>
<A NAME="2290"></A>
<P><A NAME="dingp14"></A>
One approach is to say that two objects are the same (have the same identity) if they have the same value. For example, two <CODE>String</CODE> objects would be the same if they represented the same sequence of <NOBR>characters:<SCRIPT>create_link(14);</SCRIPT>
</NOBR></P>
<A NAME="2291"></A>
<UL><PRE>String a = "Hello";
String b = "World";
String c = "Hello";
</PRE>
</UL><A NAME="2292"></A>
<P><A NAME="dingp15"></A>
Here <CODE>a</CODE> and <CODE>c</CODE> have the same value, so they are considered identical; <CODE>b</CODE> is different from both of them. If you wanted to use this definition of identity in your <CODE>String</CODE> class, your assignment operator might look like <NOBR>this:<SCRIPT>create_link(15);</SCRIPT>
</NOBR></P>
<A NAME="2293"></A>
<UL><PRE>String& String::operator=(const String& rhs)
{
if (strcmp(data, rhs.data) == 0) return *this;
</PRE>
</UL><A NAME="2294"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="2295"></A>
<UL><PRE>}
</PRE>
</UL><A NAME="2296"></A>
<P><A NAME="dingp16"></A>
Value equality is usually determined by <CODE>operator==</CODE>, so the general form for an assignment operator for a class <CODE>C</CODE> that uses value equality for object identity is <NOBR>this:<SCRIPT>create_link(16);</SCRIPT>
</NOBR></P>
<A NAME="2297"></A>
<UL><PRE>C& C::operator=(const C& rhs)
{
// check for assignment to self
if (*this == rhs) // assumes op== exists
return *this;
</PRE>
</UL><A NAME="2298"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="2299"></A>
<UL><PRE>}
</PRE>
</UL><A NAME="2300"></A>
<P><A NAME="dingp17"></A>
Note that this function is comparing <i>objects</i> (via <CODE>operator==</CODE>), not pointers. Using value equality to determine identity, it doesn't matter whether two objects occupy the same memory; all that matters is the values they <NOBR>represent.<SCRIPT>create_link(17);</SCRIPT>
</NOBR></P>
<A NAME="2301"></A>
<P><A NAME="dingp18"></A>
<A NAME="p75"></A>The other possibility is to equate an object's identity with its address in memory. Using this definition of object equality, two objects are the same if and only if they have the same address. This definition is more common in C++ programs, probably because it's easy to implement and the computation is fast, neither of which is always true when object identity is based on values. Using address equality, a general assignment operator looks like <NOBR>this:<SCRIPT>create_link(18);</SCRIPT>
</NOBR></P>
<A NAME="2302"></A>
<UL><PRE>C& C::operator=(const C& rhs)
{
// check for assignment to self
if (this == &rhs) return *this;
</PRE>
</UL><A NAME="2303"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="2304"></A>
<UL><PRE>}
</PRE>
</UL><A NAME="2305"></A>
<P><A NAME="dingp19"></A>
This suffices for a great many <NOBR>programs.<SCRIPT>create_link(19);</SCRIPT>
</NOBR></P>
<A NAME="2312"></A>
<P><A NAME="dingp20"></A>
If you need a more sophisticated mechanism for determining whether two objects are the same, you'll have to implement it yourself. The most common approach is based on a member function that returns some kind of object <NOBR>identifier:<SCRIPT>create_link(20);</SCRIPT>
</NOBR></P>
<A NAME="2313"></A>
<UL><PRE>class C {
public:
ObjectID identity() const; // see also <A HREF="./EI36_FR.HTM#7007" TARGET="_top">Item 36</A>
</PRE>
</UL><A NAME="2314"></A>
<UL><PRE> ...
</PRE>
</UL><A NAME="2315"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="2316"></A>
<P><A NAME="dingp21"></A>
Given object pointers <CODE>a</CODE> and <CODE>b</CODE>, then, the objects they point to are identical if and only if <CODE>a->identity() == b->identity()</CODE>. Of course, you are responsible for writing <CODE>operator==</CODE> for <CODE>ObjectID</CODE>s.<SCRIPT>create_link(21);</SCRIPT>
</P>
<A NAME="2317"></A>
<P><A NAME="dingp22"></A>
The problems of aliasing and object identity are hardly confined to <CODE>operator=</CODE>. That's just a function in which you are particularly likely to run into them. In the presence of references and pointers, any two names for objects of compatible types may in fact refer to the same object. Here are some other situations in which aliasing can show its Medusa-like <NOBR>visage:<SCRIPT>create_link(22);</SCRIPT>
</NOBR></P>
<A NAME="2318"></A>
<UL><PRE>class Base {
void mf1(Base& rb); // rb and *this could be
// the same
...
</PRE>
</UL><A NAME="2319"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="2320"></A>
<UL><PRE>
void f1(Base& rb1,Base& rb2); // rb1 and rb2 could be
// the same
</PRE>
</UL><A NAME="2321"></A>
<UL><PRE><A NAME="p76"></A>
class Derived: public Base {
void mf2(Base& rb); // rb and *this could be
// the same
...
</PRE>
</UL><A NAME="2322"></A>
<UL><PRE>};
</PRE>
</UL><A NAME="2323"></A>
<UL><PRE>
int f2(Derived& rd, Base& rb); // rd and rb could be
// the same
</PRE>
</UL><A NAME="2324"></A>
<P><A NAME="dingp23"></A>
These examples happen to use references, but pointers would serve just as <NOBR>well.<SCRIPT>create_link(23);</SCRIPT>
</NOBR></P>
<A NAME="2325"></A>
<P><A NAME="dingp24"></A>
As you can see, aliasing can crop up in a variety of guises, so you can't just forget about it and hope you'll never run into it. Well, maybe <I>you</I> can, but most of us can't. At the expense of mixing my metaphors, this is a clear case in which an ounce of prevention is worth its weight in gold. Anytime you write a function in which aliasing could conceivably be present, you <I>must</I> take that possibility into account when you write the <NOBR>code.<SCRIPT>create_link(24);</SCRIPT>
</NOBR></P>
<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI16_FR.HTM" TARGET="_top">Item 16: Assign to all data members in operator=.</A> <BR> Continue to <A HREF="./EDESGNFR.HTM" TARGET="_top">Classes and Functions: Design and Declaration</A></FONT></DIV>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -