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

📄 mi30.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 3 页
字号:
s2[5] = 'x';             // write s2
<A NAME="25169"></A>
s1[3] = s2[8];           // write s1, read s2
</PRE>
</UL>

<A NAME="dingp28"></A><P><A NAME="24883"></A>
Note that <CODE>operator[]</CODE> can be called in two different contexts: to read a character or to write a character. Reads are known as <I>rvalue</I> usages; writes are known as <I>lvalue</I> usages. (The terms come from the field of compilers, where an lvalue goes on the left-hand side of an assignment and an rvalue goes on the right-hand side.) In general, using an object as an lvalue means using it such that it might be modified, and using it as an rvalue means using it such that it cannot be modified.
<SCRIPT>create_link(28);</SCRIPT>
</P>

<A NAME="dingp29"></A><P><A NAME="25189"></A>
We'd like to distinguish between lvalue and rvalue usage of <CODE>operator[]</CODE> because, especially for reference-counted data structures, reads can be much less expensive to implement than writes. As <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A> ex<A NAME="p218"></A>plains, writes of reference-counted objects may involve copying an entire data structure, but reads never require more than the simple returning of a value. Unfortunately, inside <CODE>operator[]</CODE>, there is no way to determine the context in which the function was called; it is not possible to distinguish lvalue usage from rvalue usage within <CODE>operator[]</CODE>.
<SCRIPT>create_link(29);</SCRIPT>
</P>

<A NAME="dingp30"></A><P><A NAME="57041"></A>
"But wait," you say, "we don't need to. We can overload <CODE>operator[]</CODE> on the basis of its <CODE>const</CODE>ness, and that will allow us to distinguish reads from writes." In other words, you suggest we solve our problem this way:
<SCRIPT>create_link(30);</SCRIPT>
</P><A NAME="57042"></A>

<UL><PRE>class String {
public:
  const char&amp; operator[](int index) const;       // for reads
  char&amp; operator[](int index);                   // for writes
  ...
<A NAME="76221"></A>
};
</PRE>
</UL>

<A NAME="dingp31"></A><P><A NAME="57031"></A>
Alas, this won't work. Compilers choose between <CODE>const</CODE> and non-<CODE>const</CODE> member functions by looking only at whether the <I>object</I> invoking a function is <CODE>const</CODE>. No consideration is given to the context in which a call is made. Hence:
<SCRIPT>create_link(31);</SCRIPT>
</P><A NAME="57062"></A>
<UL><PRE>String s1, s2;
<A NAME="57063"></A>
...
<A NAME="57064"></A>
cout &lt;&lt; s1[5];                        // calls non-const operator[],
                                      // because s1 isn't const
<A NAME="57065"></A>
s2[5] = 'x';                          // also calls non-const
                                      // operator[]: s2 isn't const
<A NAME="57066"></A>
s1[3] = s2[8];                        // both calls are to non-const
                                      // operator[], because both s1
                                      // and s2 are non-const objects
</PRE>
</UL><A NAME="dingp32"></A><P><A NAME="57060"></A>
Overloading <CODE>operator[]</CODE>, then, fails to distinguish reads from writes.
<SCRIPT>create_link(32);</SCRIPT>
</P>

<A NAME="dingp33"></A><P><A NAME="25215"></A>
In <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>, we resigned ourselves to this unsatisfactory state of affairs and made the conservative assumption that all calls to <CODE>operator[]</CODE> were for writes. This time we shall not give up so easily. It may be impossible to distinguish lvalue from rvalue usage inside <CODE>operator[]</CODE>, but we still want to do it. We will therefore find a way. What fun is life if you allow yourself to be limited by the possible?
<SCRIPT>create_link(33);</SCRIPT>
</P>

<A NAME="dingp34"></A><P><A NAME="25240"></A>
Our approach is based on the fact that though it may be impossible to tell whether <CODE>operator[]</CODE> is being invoked in an lvalue or an rvalue context from within <CODE>operator[]</CODE>, we can still treat reads differently from writes if we <I>delay</I> our lvalue-versus-rvalue actions until we see <A NAME="p219"></A>how the result of <CODE>operator[]</CODE> is used. All we need is a way to postpone our decision on whether our object is being read or written until <i>after</i> <CODE>operator[]</CODE> has returned. (This is an example of <I>lazy evaluation</I> &#151; see <a href="./MI17_FR.HTM#41011" TARGET="_top">Item 17</A>.)
<SCRIPT>create_link(34);</SCRIPT>
</P>

<A NAME="dingp35"></A><P><A NAME="25478"></A>
A proxy class allows us to buy the time we need, because we can modify <CODE>operator[]</CODE> to return a <I>proxy</I> for a string character instead of a string character itself. We can then wait to see how the proxy is used. If it's read, we can belatedly treat the call to <CODE>operator[]</CODE> as a read. If it's written, we must treat the call to <CODE>operator[]</CODE> as a write.
<SCRIPT>create_link(35);</SCRIPT>
</P>

<A NAME="dingp36"></A><P><A NAME="25310"></A>
We will see the code for this in a moment, but first it is important to understand the proxies we'll be using. There are only three things you can do with a proxy:<SCRIPT>create_link(36);</SCRIPT>
</P>
<UL><A NAME="25287"></A>

<A NAME="dingp37"></A><LI>Create it, i.e., specify which string character it stands for.<SCRIPT>create_link(37);</SCRIPT>

<A NAME="25314"></A>

<A NAME="dingp38"></A><LI>Use it as the target of an assignment, in which case you are really making an assignment to the string character it stands for. When used in this way, a proxy represents an lvalue use of the string on which <CODE>operator[]</CODE> was invoked.<SCRIPT>create_link(38);</SCRIPT>

<A NAME="25318"></A>

<A NAME="dingp39"></A><LI>Use it in any other way. When used like this, a proxy represents an rvalue use of the string on which <CODE>operator[]</CODE> was invoked.<SCRIPT>create_link(39);</SCRIPT>

</UL>

<A NAME="dingp40"></A><P><A NAME="25328"></A>
Here are the class definitions for a reference-counted <CODE>String</CODE> class using a proxy class to distinguish between lvalue and rvalue usages of <CODE>operator[]</CODE>:<SCRIPT>create_link(40);</SCRIPT>
</P>
<A NAME="25339"></A>

<UL><PRE>
class String {                    // reference-counted strings;
public:                           // see <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A> for details
<A NAME="57829"></A>
  class CharProxy {               // proxies for string chars
  public:
    CharProxy(String&amp; str, int index);                // creation
<A NAME="58869"></A>
    CharProxy&amp; operator=(const CharProxy&amp; rhs);       // lvalue
    CharProxy&amp; operator=(char c);                     // uses
<A NAME="58870"></A>
    operator char() const;                            // rvalue
                                                      // use
  private:
    String&amp; theString;            // string this proxy pertains to
<A NAME="58888"></A>
    int charIndex;                // char within that string
                                  // this proxy stands for
  };
<A NAME="70827"></A>
  // continuation of String class
  const CharProxy
    operator[](int index) const;   // for const Strings
<A NAME="57283"></A>
<A NAME="p220"></A>
  CharProxy operator[](int index); // for non-const Strings
  ...
<A NAME="57834"></A>
friend class CharProxy;
<A NAME="71848"></A>
private:
  RCPtr&lt;StringValue&gt; value;
};
</PRE>
</UL>

<A NAME="dingp41"></A><P><A NAME="25342"></A>
Other than the addition of the <CODE>CharProxy</CODE> class (which we'll examine below), the only difference between this <CODE>String</CODE> class and the final <CODE>String</CODE> class in <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A> is that both <CODE>operator[]</CODE> functions now return <CODE>CharProxy</CODE> objects. Clients of <CODE>String</CODE> can generally ignore this, however, and program as if the <CODE>operator[]</CODE> functions returned characters (or references to characters &#151; see<a href="./MI1_FR.HTM#11029" TARGET="_top"> Item 1</A>) in the usual manner:
<SCRIPT>create_link(41);</SCRIPT>
</P>

<A NAME="25396"></A>
<UL><PRE>
String s1, s2;           // reference-counted strings
                         // using proxies
...
<A NAME="25398"></A>
cout &lt;&lt; s1[5];           // still legal, still works
<A NAME="25399"></A>
s2[5] = 'x';             // also legal, also works
<A NAME="25400"></A>
s1[3] = s2[8];           // of course it's legal,
                         // of course it works
</PRE>
</UL>

<A NAME="dingp42"></A><P><A NAME="25413"></A>
What's interesting is not that this works. What's interesting is <i>how</i> it works.
<SCRIPT>create_link(42);</SCRIPT>
</P>

<A NAME="dingp43"></A><P><A NAME="25507"></A>
Consider first this statement:
<SCRIPT>create_link(43);</SCRIPT>
</P><A NAME="25509"></A>

<UL><PRE>cout &lt;&lt; s1[5];
</PRE>
</UL>

<A NAME="dingp44"></A><P><A NAME="25514"></A>
The expression <CODE>s1[5]</CODE> yields a <CODE>CharProxy</CODE> object. No output operator is defined for such objects, so your compilers labor to find an implicit type conversion they can apply to make the call to <CODE>operator&lt;&lt;</CODE> succeed (see <a href="./MI5_FR.HTM#5970" TARGET="_top">Item 5</A>). They find one: the implicit conversion from <CODE>CharProxy</CODE> to <CODE>char</CODE> declared in the <CODE>CharProxy</CODE> class. They automatically invoke this conversion operator, and the result is that the string character represented by the <CODE>CharProxy</CODE> is printed. This is representative of the <CODE>CharProxy</CODE>-to-<CODE>char</CODE> conversion that takes place for all <CODE>CharProxy</CODE> objects used as rvalues.
<SCRIPT>create_link(44);</SCRIPT>
</P>

<A NAME="dingp45"></A><P><A NAME="25524"></A>
Lvalue usage is handled differently. Look again at
<SCRIPT>create_link(45);</SCRIPT>
</P><A NAME="24879"></A>

<UL><PRE>s2[5] = 'x';
</PRE>
</UL>

<A NAME="dingp46"></A><P><A NAME="25625"></A>
As before, the expression <CODE>s2[5]</CODE> yields a <CODE>CharProxy</CODE> object, but this time that object is the target of an assignment. Which assignment operator is invoked? The target of the assignment is a <CODE>CharProxy</CODE>, so the assignment operator that's called is in the <CODE>CharProxy</CODE> class. This is crucial, because inside a <CODE>CharProxy</CODE> assignment operator, we know <A NAME="p221"></A>that the <CODE>CharProxy</CODE> object being assigned to is being used as an lvalue. We therefore know that the string character for which the proxy stands is being used as an lvalue, and we must take whatever actions are necessary to implement lvalue access for that character.
<SCRIPT>create_link(46);</SCRIPT>
</P>

<A NAME="dingp47"></A><P><A NAME="58897"></A>
Similarly, the statement
<SCRIPT>create_link(47);</SCRIPT>
</P><A NAME="25578"></A>

<UL><PRE>s1[3] = s2[8];
</PRE>
</UL>

<A NAME="dingp48"></A><P><A NAME="25596"></A>
calls the assignment operator for two <CODE>CharProxy</CODE> objects, and inside that operator we know the object on the left is being used as an lvalue and the object on the right as an rvalue.
<SCRIPT>create_link(48);</SCRIPT>
</P>

<A NAME="dingp49"></A><P><A NAME="25667"></A>
"Yeah, yeah, yeah," you grumble, "show me." Okay. Here's the code for <CODE>String</CODE>'s <CODE>operator[]</CODE> functions:
<SCRIPT>create_link(49);</SCRIPT>
</P>

<A NAME="25672"></A>
<UL><PRE>const String::CharProxy String::operator[](int index) const
{
  return CharProxy(const_cast&lt;String&amp;&gt;(*this), index);
}
</PRE>
</UL><A NAME="25669"></A>
<UL><PRE>String::CharProxy String::operator[](int index)
{
  return CharProxy(*this, index);
}
</PRE>
</UL>

<A NAME="dingp50"></A><P><A NAME="25670"></A>
Each function just creates and returns a proxy for the requested character. No action is taken on the character itself: we defer such action until we know whether the access is for a read or a write.
<SCRIPT>create_link(50);</SCRIPT>
</P>

<A NAME="dingp51"></A><P><A NAME="57263"></A>
Note that the <CODE>const</CODE> version of <CODE>operator[]</CODE> returns a <CODE>const</CODE> proxy. Because <CODE>CharProxy::operator=</CODE> isn't a <CODE>const</CODE> member function, such proxies can't be used as the target of assignments. Hence neither the proxy returned from the <CODE>const</CODE> version of <CODE>operator[]</CODE> nor the character for which it stands may be used as an lvalue. Conveniently enough, that's exactly the behavior we want for the <CODE>const</CODE> version of <CODE>operator[]</CODE>.
<SCRIPT>create_link(51);</SCRIPT>
</P>    <A NAME="dingp52"></A><P><A NAME="57294"></A>
Note also the use of a <CODE>const_cast</CODE> (see<a href="./MI2_FR.HTM#77216" TARGET="_top"> Item 2</A>) on <CODE>*this</CODE> when creating the <CODE>CharProxy</CODE> object that the <CODE>const</CODE> <CODE>operator[]</CODE> returns. That's necessary to satisfy the constraints of the <CODE>CharProxy</CODE> constructor, which accepts only a non-<CODE>const</CODE> <CODE>String</CODE>. Casts are usually worrisome, but in this case the <CODE>CharProxy</CODE> object returned by <CODE>operator[]</CODE> is itself <CODE>const</CODE>, so there is no risk the <CODE>String</CODE> containing the character to which the proxy refers will be modified.
<SCRIPT>create_link(52);</SCRIPT>
</P>    <A NAME="dingp53"></A><P><A NAME="25698"></A>
Each proxy returned by an <CODE>operator[]</CODE> function remembers which string it pertains to and, within that string, the index of the character it represents:
<SCRIPT>create_link(53);</SCRIPT>
</P><A NAME="25679"></A>
<UL><PRE><A NAME="p222"></A>String::CharProxy::CharProxy(String&amp; str, int index)
: theString(str), charIndex(index) {}
</PRE>
</UL><P><A NAME="25597"></A>
<A NAME="dingp54"></A>Conversion of a proxy to an rvalue is straightforward &#151; we just return a copy of the character represented by the proxy:<SCRIPT>create_link(54);</SCRIPT>
</P><A NAME="25722"></A>
<UL><PRE>String::CharProxy::operator char() const
{
  return theString.value-&gt;data[charIndex];
}
</PRE>
</UL><A NAME="dingp55"></A><P><A NAME="24881"></A>
If you've forgotten the relationship among a <CODE>String</CODE> object, its <CODE>value</CODE> member, and the <CODE>data</CODE> member it points to, you can refresh your memory by turning to <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>. Because this function returns a character by value, and because C++ limits the use of such by-value returns to rvalue contexts only, this conversion function can be used only in places where an rvalue is legal.
<SCRIPT>create_link(55);</SCRIPT>
</P>    <A NAME="dingp56"></A><P><A NAME="24645"></A>
We thus turn to implementation of <CODE>CharProxy</CODE>'s assignment operators, which is where we must deal with the fact that a character represented by a proxy is being used as the target of an assignment, i.e., as an lvalue. We can implement <CODE>CharProxy</CODE>'s conventional assignment operator as follows:
<SCRIPT>create_link(56);</SCRIPT>
</P><A NAME="58988"></A>
<UL><PRE>String::CharProxy&amp;
String::CharProxy::operator=(const CharProxy&amp; rhs)
{
  // if the string is sharing a value with other String objects,
  // break off a separate copy of the value for this string only
  if (theString.value-&gt;isShared()) {
    theString.value = new StringValue(theString.value-&gt;data);
  }
<A NAME="58989"></A>
  // now make the assignment: assign the value of the char
  // represented by rhs to the char represented by *this
  theString.value-&gt;data[charIndex] =
    rhs.theString.value-&gt;data[rhs.charIndex];
<A NAME="58990"></A>
  return *this;
}
</PRE>
</UL><A NAME="dingp57"></A><P><A NAME="25772"></A>
If you compare this with the implementation of the <a href="./MI29_FR.HTM#18667" TARGET="_top">non-<CODE>const String</CODE>::<CODE>operator</CODE></a> in <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>, you'll see that they are strikingly similar. This is to be expected. In <a href="./MI29_FR.HTM#6073" TARGET="_top">Item 29</A>, we pessimistically assumed that all invocations of the non-<CODE>const</CODE> <CODE>operator[]</CODE> were writes, so we treated them as such. Here, we moved the code implementing a write into <CODE>CharProxy</CODE>'s assignment operators, and that allows us to avoid paying for a write when the non-<CODE>const</CODE> <CODE>operator[]</CODE> is used only in an rvalue context. Note, by the way, that this function requires access to <CODE>String</CODE>'s private data member <CODE>value. </CODE>That's why <A NAME="p223"></A><CODE>CharProxy</CODE> is declared a friend in the earlier class definition for <CODE>String</CODE>.
<SCRIPT>create_link(57);</SCRIPT>
</P>    <A NAME="dingp58"></A><P><A NAME="58998"></A>

⌨️ 快捷键说明

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