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

📄 ei21.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<A NAME="6085"></A>
<P><A NAME="dingp21"></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(21);</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="dingp22"></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="./EI29_FR.HTM#6490" TARGET="_top">Item 29</A>.)<SCRIPT>create_link(22);</SCRIPT>
</P>
<A NAME="222159"></A>
<P><A NAME="dingp23"></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/MI18_FR.HTM#41124" TARGET="_top">Item M18</A>):<SCRIPT>create_link(23);</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="dingp24"></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(24);</SCRIPT>
</NOBR></P>
<A NAME="16408"></A>
<P><A NAME="dingp25"></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(25);</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="dingp26"></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(26);</SCRIPT>
</NOBR></P>
<A NAME="16462"></A>
<P><A NAME="dingp27"></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(27);</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="dingp28"></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(28);</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="dingp29"></A>
Pretty this ain't, but sometimes a programmer's just gotta do what a programmer's gotta <NOBR>do.<SCRIPT>create_link(29);</SCRIPT>
</NOBR></P>
<A NAME="6123"></A>
<P><A NAME="dingp30"></A>
Unless, of course, it's not guaranteed to work, and sometimes the old cast-away-constness trick isn't. In particular, if the object <CODE>this</CODE> points to is truly <CODE>const</CODE>, i.e., was declared <CODE>const</CODE> at its point of definition, the results of casting away its constness are undefined. If you want to cast away constness in one of your member functions, you'd best be sure that the object you're doing the casting on wasn't originally defined to be <CODE>const</CODE>.<SCRIPT>create_link(30);</SCRIPT>
</P>
<A NAME="6124"></A>
<P><A NAME="dingp31"></A>
There is one other time when casting away constness may be both useful and safe. That's when you have a <CODE>const</CODE> object you want to pass to a function taking a non-<CODE>const</CODE> parameter, and <I>you know the parameter won't be modified inside the function</I>. The second condition is important, because it is always safe to cast away the constness of an object that will only be read &#151; not written &#151; even if that object was originally defined to be <CODE>const</CODE>.<SCRIPT>create_link(31);</SCRIPT>
</P>
<A NAME="6125"></A>
<P><A NAME="dingp32"></A>
For example, some libraries have been known to incorrectly declare the <CODE>strlen</CODE> function as <NOBR>follows:<SCRIPT>create_link(32);</SCRIPT>
</NOBR></P>
<A NAME="6126"></A>
<UL><PRE>size_t strlen(char *s);
</PRE>
</UL><A NAME="6127"></A>
<P><A NAME="dingp33"></A>
Certainly <CODE>strlen</CODE> isn't going to modify what <CODE>s</CODE> points to &#151; at least not the <CODE>strlen</CODE> I grew up with. Because of this declaration, however, it would be invalid to call it on pointers of type <CODE>const</CODE> <CODE>char</CODE> <CODE>*</CODE>. To get around the problem, you can safely cast away the constness of such pointers when you pass them to <CODE>strlen</CODE>:<SCRIPT>create_link(33);</SCRIPT>
</P>
<A NAME="6128"></A>
<UL><PRE>
const char *klingonGreeting = "nuqneH";        // "nuqneH" is
                                               // "Hello" in
                                               // Klingon
size_t length =
  strlen(const_cast&lt;char*&gt;(klingonGreeting));
</PRE>
</UL><A NAME="6130"></A>
<P><A NAME="dingp34"></A>
Don't get cavalier about this, though. It is guaranteed to work only if the function being called, <CODE>strlen</CODE> in this case, doesn't try to modify what its parameter points <NOBR>to.<SCRIPT>create_link(34);</SCRIPT>
</NOBR></P>

<DIV ALIGN="CENTER"><FONT SIZE="-1">Back to <A HREF="./EI20_FR.HTM" TARGET="_top">Item 20: Differentiate among member functions, non-member functions, and friend functions.</A> &nbsp;&nbsp;<BR>&nbsp;&nbsp;Continue to <A HREF="./EI22_FR.HTM" TARGET="_top">Item 22: Prefer pass-by-reference to pass-by-value.</A></FONT></DIV>
<HR WIDTH="100%">
<A NAME="dingp35"></A><A NAME="223676"></A><sup>5</sup> According to the <NOBR><FONT COLOR="#FF0000" SIZE="-2"><B>&deg;</B></FONT><A HREF="http://www.awl.com/cseng/cgi-bin/cdquery.pl?name=cstandard" onMouseOver = "self.status = 'The latest publicly-available version of the C++ standard'; return true" onMouseOut = "self.status = self.defaultStatus" TARGET="_top">C++</NOBR> standard</A>, the type of <CODE>"Hello"</CODE> is <CODE>const char []</CODE>, a type that's almost always treated as <CODE>const char*</CODE>. We'd therefore expect it to be a violation of <CODE>const</CODE> correctness to initialize a <CODE>char*</CODE> variable with a string literal like <CODE>"Hello"</CODE>. The practice is so common in C, however, that the standard grants a special dispensation for initializations like this. Nevertheless, you should try to avoid them, because they're deprecated.<SCRIPT>create_link(35);</SCRIPT>
<BR>
<A HREF="#6017">Return</A>

</BODY>
</HTML>

⌨️ 快捷键说明

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