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

📄 8815.htm

📁 C++细节解释
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<p>cs[0] = 'x';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 错误!——写一个<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // const string</p> 
<p>另外注意,这里的错误只和调用operator[]的返回值有关;operator[]调用本身没问题。 错误产生的原因在于企图对一个const char&amp;赋值,因为被赋值的对象是const版本的operator[]函数的返回值。</p> 
<p>还要注意,非const operator[]的返回类型必须是一个char的引用——char本身则不行。如果operator[]真的返回了一个简单的char,如下所示的语句就不会通过编译:</p> 
<p>s[0] = 'x';</p> 
<p>因为,修改一个“返回值为固定类型”的函数的返回值绝对是不合法的。即使合法,由于c++“通过值(而不是引用)来返回对象”(见条款22)的内部机制的原因,s.data[0]的一个拷贝会被修改,而不是s.data[0]自己,这就不是你所想要的结果了。</p> 
<p>让我们停下来看一个基本原理。一个成员函数为const的确切含义是什么?有两种主要的看法:数据意义上的const(bitwise constness)和概念意义上的const(conceptual constness)。</p> 
<p>bitwise constness的坚持者认为,当且仅当成员函数不修改对象的任何数据成员(静态数据成员除外)时,即不修改对象中任何一个比特(bit)时,这个成员函数才是const的。bitwise constness最大的好处是可以很容易地检测到违反bitwise constness规定的事件:编译器只用去寻找有无对数据成员的赋值就可以了。实际上,bitwise constness正是c++对const问题的定义,const成员函数不被允许修改它所在对象的任何一个数据成员。</p> 
<p>不幸的是,很多不遵守bitwise constness定义的成员函数也可以通过bitwise测试。特别是,一个“修改了指针所指向的数据”的成员函数,其行为显然违反了bitwise constness定义,但如果对象中仅包含这个指针,这个函数也是bitwise const的,编译时会通过。这就和我们的直觉有差异:</p> 
<p>class string {<br>public:<br>&nbsp; // 构造函数,使data指向一个<br>&nbsp; // value所指向的数据的拷贝<br>&nbsp; string(const char *value);</p> 
<p>&nbsp; ...</p> 
<p>&nbsp; operator char *() const { return data;}</p> 
<p>private:<br>&nbsp; char *data;<br>};</p> 
<p>const string s = "hello";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 声明常量对象</p> 
<p>char *nasty = s;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 调用 operator char*() const</p> 
<p>*nasty = 'm';&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 修改s.data[0]</p> 
<p>cout &lt;&lt; s;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 输出"mello"</p> 
<p>显然,在用一个值创建一个常量对象并调用对象的const成员函数时一定有什么错误,对象的值竟然可以修改!(关于这个例子更详细的讨论参见条款29)</p> 
<p>这就导致conceptual constness观点的引入。此观点的坚持者认为,一个const成员函数可以修改它所在对象的一些数据(bits) ,但只有在用户不会发觉的情况下。例如,假设string类想保存对象每次被请求时数据的长度:</p> 
<p>class string {<br>public:<br>&nbsp; // 构造函数,使data指向一个<br>&nbsp; // value所指向的数据的拷贝<br>&nbsp; string(const char *value): lengthisvalid(false) { ... }</p> 
<p>&nbsp; ...</p> 
<p>&nbsp; size_t length() const;</p> 
<p>private:<br>&nbsp; char *data;</p> 
<p>&nbsp; size_t datalength;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 最后计算出的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // string的长度</p> 
<p>&nbsp; bool lengthisvalid;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 长度当前<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 是否合法<br>};</p> 
<p>size_t string::length() const<br>{<br>&nbsp; if (!lengthisvalid) {<br>&nbsp;&nbsp;&nbsp; datalength = strlen(data); // 错误!<br>&nbsp;&nbsp;&nbsp; lengthisvalid = true;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 错误!<br>&nbsp; }</p> 
<p>&nbsp; return datalength;<br>}</p> 
<p>这个length的实现显然不符合“bitwise const”的定义——datalength 和lengthisvalid都可以修改——但对const string对象来说,似乎它一定要是合法的才行。但编译器也不同意, 它们坚持“bitwise constness”,怎么办?</p> 
<p>解决方案很简单:利用c++标准组织针对这类情况专门提供的有关const问题的另一个可选方案。此方案使用了关键字mutable,当对非静态数据成员运用mutable时,这些成员的“bitwise constness”限制就被解除:</p> 
<p>class string {<br>public:</p> 
<p>&nbsp; ...&nbsp;&nbsp;&nbsp; // same as above</p> 
<p>private:<br>&nbsp; char *data;</p> 
<p>&nbsp; mutable size_t datalength;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 这些数据成员现在<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 为mutable;他们可以在<br>&nbsp; mutable bool lengthisvalid;&nbsp;&nbsp;&nbsp;&nbsp; // 任何地方被修改,即使<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 在const成员函数里<br>}; </p> 
<p>size_t string::length() const<br>{<br>&nbsp; if (!lengthisvalid) {<br>&nbsp;&nbsp;&nbsp; datalength = strlen(data);&nbsp;&nbsp;&nbsp; // 现在合法<br>&nbsp;&nbsp;&nbsp; lengthisvalid = true;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 同样合法<br>&nbsp; }</p> 
<p>&nbsp; return datalength;<br>}</p> 
<p>mutable在处理“bitwise-constness限制”问题时是一个很好的方案,但它被加入到c++标准中的时间不长,所以有的编译器可能还不支持它。如果是这样,就不得不倒退到c++黑暗的旧时代去,在那儿,生活很简陋,const有时可能会被抛弃。</p> 
<p>类c的一个成员函数中,this指针就好象经过如下的声明:</p> 
<p>c * const this;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 非const成员函数中</p> 
<p>const c * const this;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // const成员函数中</p> 
<p>这种情况下(即编译器不支持mutable的情况下),如果想使那个有问题的string::length版本对const和非const对象都合法,就只有把this的类型从const c * const改成c * const。不能直接这么做,但可以通过初始化一个局部变量指针,使之指向this所指的同一个对象来间接实现。然后,就可以通过这个局部指针来访问你想修改的成员:</p> 
<p>size_t string::length() const<br>{<br>&nbsp; // 定义一个不指向const对象的<br>&nbsp; // 局部版本的this指针<br>&nbsp; string * const localthis =<br>&nbsp;&nbsp;&nbsp; const_cast&lt;string * const&gt;(this);</p> 
<p>&nbsp; if (!lengthisvalid) {<br>&nbsp;&nbsp;&nbsp; localthis-&gt;datalength = strlen(data);<br>&nbsp;&nbsp;&nbsp; localthis-&gt;lengthisvalid = true;<br>&nbsp; }</p> 
<p>&nbsp; return datalength;<br>}</p> 
<p>做的不是很漂亮。但为了完成想要的功能也就只有这么做。</p> 
<p>当然,如果不能保证这个方法一定可行,就不要这么做:比如,一些老的“消除const”的方法就不行。特别是,如果this所指的对象真的是const,即,在定义时被声明为const,那么,“消除const”就会导致不可确定的后果。所以,如果想在成员函数中通过转换消除const,就最好先确信你要转换的对象最初没有被定义为const。</p> 
<p>还有一种情况下,通过类型转换消除const会既有用又安全。这就是:将一个const对象传递到一个取非const参数的函数中,同时你又知道参数不会在函数内部被修改的情况时。第二个条件很重要,因为对一个只会被读的对象(不会被写)消除const永远是安全的,即使那个对象最初曾被定义为const。</p> 
<p>例如,已经知道有些库不正确地声明了象下面这样的strlen函数:</p> 
<p>size_t strlen(char *s);</p> 
<p>strlen当然不会去修改s所指的数据——至少我一辈子没看见过。但因为有了这个声明,对一个const char *类型的指针调用这个函数时就会不合法。为解决这个问题,可以在给strlen传参数时安全地把这个指针的const强制转换掉:</p> 
<p>const char *klingongreeting = "nuqneh"; // "nuqneh"即"hello"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <br>size_t length =<br>&nbsp; strlen(const_cast&lt;char*&gt;(klingongreeting));</p> 
<p>但不要滥用这个方法。只有在被调用的函数(比如本例中的strlen)不会修改它的参数所指的数据时,才能保证它可以正常工作。<br> 
</p> 
</DIV></div></div> 
 
</center></BODY></HTML> 

⌨️ 快捷键说明

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