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

📄 ec4.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 5 页
字号:
cs[0] = 'x';                             // error! — writing a
                                         // const String
</PRE>
</UL><A NAME="6071"></A>
<P><A NAME="dingp102"></A>
By the way, note that the error here has only to do with the <I>return value</I> of the <CODE>operator[]</CODE> that is called; the calls to <CODE>operator[]</CODE> themselves are all fine. The error arises out of an attempt to make an assignment to a <CODE>const</CODE> <CODE>char&amp;</CODE>, because that's the return value from the <CODE>const</CODE> version of <CODE>operator[]</CODE>.<SCRIPT>create_link(102);</SCRIPT>
</P>
<A NAME="6075"></A>
<P><A NAME="dingp103"></A>
Also note that the return type of the non-<CODE>const</CODE> <CODE>operator[]</CODE> must be a <I>reference</I> to a <CODE>char</CODE> &#151; a <CODE>char</CODE> itself will not do. If <CODE>operator[]</CODE> did return a simple <CODE>char</CODE>, statements like this wouldn't <NOBR>compile:<SCRIPT>create_link(103);</SCRIPT>
</NOBR></P>
<A NAME="6076"></A>
<UL><PRE>s[0] = 'x';
</PRE>
</UL><A NAME="6077"></A>
<P><A NAME="dingp104"></A>
<A NAME="p94"></A>That's because it's never legal to modify the return value of a function that returns a built-in type. Even if it were legal, the fact that C++ returns objects by value (see <A HREF="#6133">Item 22</A>) would mean that a <I>copy</I> of <CODE>s.data[0]</CODE> would be modified, not <CODE>s.data[0]</CODE> itself, and that's not the behavior you want, <NOBR>anyway.<SCRIPT>create_link(104);</SCRIPT>
</NOBR></P>
<A NAME="6081"></A>
<P><A NAME="dingp105"></A>
Let's take a brief time-out for philosophy. What exactly does it mean for a member function to be <CODE>const</CODE>? There are two prevailing notions: bitwise constness and conceptual <NOBR>constness.<SCRIPT>create_link(105);</SCRIPT>
</NOBR></P>
<A NAME="6084"></A>
<P><A NAME="dingp106"></A>
The bitwise <CODE>const</CODE> camp believes that a member function is <CODE>const</CODE> if and only if it doesn't modify any of the object's data members (excluding those that are static), i.e., if it doesn't modify any of the bits inside the object. The nice thing about bitwise constness is that it's easy to detect violations: compilers just look for assignments to data members. In fact, bitwise constness is C++'s definition of constness, and a <CODE>const</CODE> member function isn't allowed to modify any of the data members of the object on which it is <NOBR>invoked.<SCRIPT>create_link(106);</SCRIPT>
</NOBR></P>
<A NAME="6085"></A>
<P><A NAME="dingp107"></A>
Unfortunately, many member functions that don't act very <CODE>const</CODE> pass the bitwise test. In particular, a member function that modifies what a pointer <I>points to</I> frequently doesn't act <CODE>const</CODE>. But if only the <I>pointer</I> is in the object, the function is bitwise <CODE>const</CODE>, and compilers won't complain. That can lead to counterintuitive <NOBR>behavior:<SCRIPT>create_link(107);</SCRIPT>
</NOBR></P>
<A NAME="222617"></A>
<UL><PRE>class String {
public:
  // the constructor makes data point to a copy
  // of what value points to
  String(const char *value);
</PRE>
</UL><A NAME="222618"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="222620"></A>
<UL><PRE>  operator char *() const { return data;}
</PRE>
</UL><A NAME="16392"></A>
<UL><PRE>private:
  char *data;
};
</PRE>
</UL><A NAME="6090"></A>
<UL><PRE>
const String s = "Hello";      // declare constant object
</PRE>
</UL><A NAME="6091"></A>
<UL><PRE>
char *nasty = s;               // calls op char*() const
</PRE>
</UL><A NAME="6092"></A>
<UL><PRE>
*nasty = 'M';                  // modifies s.data[0]
</PRE>
</UL><A NAME="6093"></A>
<UL><PRE>
cout &lt;&lt; s;                     // writes "Mello"
</PRE>
</UL><A NAME="132400"></A>
<P><A NAME="dingp108"></A>
Surely there is something wrong when you create a constant object with a particular value and you invoke only <CODE>const</CODE> member functions on it, yet you are still able to change its value! (For a more detailed discussion of this example, see <A HREF="./EC5_FR.HTM#6490" TARGET="_top">Item 29</A>.)<SCRIPT>create_link(108);</SCRIPT>
</P>
<A NAME="222159"></A>
<P><A NAME="dingp109"></A>
<A NAME="p95"></A><A NAME="p95"></A>This leads to the notion of conceptual constness. Adherents to this philosophy argue that a <CODE>const</CODE> member function might modify some of the bits in the object on which it's invoked, but only in ways that are undetectable by clients. For example, your <CODE>String</CODE> class might want to cache the length of the object whenever it's requested (see <A HREF="../MEC/MC4_FR.HTM#41124" TARGET="_top">Item M18</A>):<SCRIPT>create_link(109);</SCRIPT>
</P>
<A NAME="222623"></A>
<UL><PRE>class String {
public:
  // the constructor makes data point to a copy
  // of what value points to
  String(const char *value): lengthIsValid(false) { ... }
</PRE>
</UL><A NAME="222624"></A>
<UL><PRE>  ...
</PRE>
</UL><A NAME="222625"></A>
<UL><PRE>  size_t length() const;
</PRE>
</UL><A NAME="16403"></A>
<UL><PRE>private:
  char *data;
</PRE>
</UL><A NAME="16401"></A>
<UL><PRE>
  size_t dataLength;                // last calculated length
                                    // of string
</PRE>
</UL><A NAME="16402"></A>
<UL><PRE>
  bool lengthIsValid;               // whether length is
                                    // currently valid
};
</PRE>
</UL><A NAME="6105"></A>
<UL><PRE>size_t String::length() const
{
  if (!lengthIsValid) {
    dataLength = strlen(data);      // error!
    lengthIsValid = true;           // error!
  }
</PRE>
</UL><A NAME="6106"></A>
<UL><PRE>  return dataLength;
}
</PRE>
</UL><A NAME="6107"></A>
<P><A NAME="dingp110"></A>
This implementation of <CODE>length</CODE> is certainly not bitwise const &#151; both <CODE>dataLength</CODE> and <CODE>lengthIsValid</CODE> may be modified &#151; yet it seems as though it should be valid for <CODE>const</CODE> <CODE>String</CODE> objects. Compilers, you will find, respectfully disagree; they insist on bitwise constness. What to <NOBR>do?<SCRIPT>create_link(110);</SCRIPT>
</NOBR></P>
<A NAME="16408"></A>
<P><A NAME="dingp111"></A>
The solution is simple: take advantage of the <CODE>const</CODE>-related wiggle room the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=committee" onMouseOver = "self.status = 'ISO ANSI Standardization Committee'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standardization committee</A> thoughtfully provided for just these types of situations. That wiggle room takes the form of the keyword <CODE>mutable</CODE>. When applied to nonstatic data members, <CODE>mutable</CODE> frees those members from the constraints of bitwise <NOBR>constness:<SCRIPT>create_link(111);</SCRIPT>
</NOBR></P>
<A NAME="16432"></A>
<UL><PRE><A NAME="p96"></A>class String {
public:
</PRE>
</UL><A NAME="16441"></A>
<UL><PRE>  ...    // same as above
</PRE>
</UL><A NAME="16434"></A>
<UL><PRE>private:
  char *data;
</PRE>
</UL><A NAME="16435"></A>
<UL><PRE>
  mutable size_t dataLength;            // these data members are
                                        // now <I>mutable</I>; they may be
  mutable bool lengthIsValid;           // modified anywhere, even
                                        // inside const member
};                                      // functions
</PRE>
</UL><A NAME="16437"></A>
<UL><PRE>size_t String::length() const
{
  if (!lengthIsValid) {
    dataLength = strlen(data);    // now fine
    lengthIsValid = true;         // also fine
  }
</PRE>
</UL><A NAME="16438"></A>
<UL><PRE>  return dataLength;
}
</PRE>
</UL><A NAME="6110"></A>
<P><A NAME="dingp112"></A>
<CODE>mutable</CODE> is a wonderful solution to the bitwise-constness-is-not-quite-what-I-had-in-mind problem, but it was added to C++ relatively late in the standardization process, so your compilers may not support it yet. If that's the case, you must descend into the dark recesses of C++, where life is cheap and constness may be cast <NOBR>away.<SCRIPT>create_link(112);</SCRIPT>
</NOBR></P>
<A NAME="16462"></A>
<P><A NAME="dingp113"></A>
Inside a member function of class <CODE>C</CODE>, the <CODE>this</CODE> pointer behaves as if it had been declared as <NOBR>follows:<SCRIPT>create_link(113);</SCRIPT>
</NOBR></P>
<A NAME="6111"></A>
<UL><PRE>
C * const this;                        // for non-const member
                                       // functions
</PRE>
</UL><A NAME="6112"></A>
<UL><PRE>
const C * const this;                  // for const member
                                       // functions
</PRE>
</UL><A NAME="6114"></A>
<P><A NAME="dingp114"></A>
That being the case, all you have to do to make the problematic version of <CODE>String::length</CODE> (i.e., the one you could fix with <CODE>mutable</CODE> if your compilers supported it) valid for both <CODE>const</CODE> and non-<CODE>const</CODE> objects is to change the type of <CODE>this</CODE> from <CODE>const</CODE> <CODE>C</CODE> <CODE>*</CODE> <CODE>const</CODE> to <CODE>C</CODE> <CODE>*</CODE> <CODE>const</CODE>. You can't do that directly, but you can fake it by initializing a local pointer to point to the same object as <CODE>this</CODE> does. Then you can access the members you want to modify through the local <NOBR>pointer:<SCRIPT>create_link(114);</SCRIPT>
</NOBR></P>
<A NAME="6116"></A>
<UL><PRE>size_t String::length() const
{
  // make a local version of this that's
  // not a pointer-to-const
  String * const localThis =
    const_cast&lt;String * const&gt;(this);
</PRE>
</UL><A NAME="6117"></A>
<UL><PRE><A NAME="p97"></A>  if (!lengthIsValid) {
    localThis-&gt;dataLength = strlen(data);
    localThis-&gt;lengthIsValid = true;
  }
</PRE>
</UL><A NAME="6118"></A>
<UL><PRE>  return dataLength;
}
</PRE>
</UL><A NAME="6119"></A>
<P><A NAME="dingp115"></A>
Pretty this ain't, but sometimes a programmer's just gotta do what a programmer's gotta <NOBR>do.<SCRIPT>create_link(115);</SCRIPT>
</NOBR></P>
<A NAME="6123"></A>
<P><A NAME="dingp116"></A>
Unless, of

⌨️ 快捷键说明

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