📄 ch02.htm
字号:
memory, and an attempt to write to it usually triggers a hardware exception. (Using an explicit cast to remove <tt>const</tt>ness does not change the physical memory properties of a variable.) For example</p> <pre><tt>const int cnum = 0; //true const, may be stored in the machine's ROM</tt><tt>const int * pci = &cnum;</tt><tt>int *pi = const_cast<int*> (pci); // brute force attempt to unconst a variable</tt><tt>cout<< *pi; //OK, value of cnum is not modified</tt><tt>*pi = 2; //undefined, an attempt to modify cnum which is a true const variable</tt></pre> <p>On the other hand, casting away the contractual <tt>const</tt>ness of an object makes it possible to modify its value safely:</p> <pre><tt> int num = 0;</tt><tt> const int * pci = &num; // *pci is a contractual const int</tt><tt> int *pi = const_cast<int*> (pci); // get rid of contractual const</tt><tt> *pi = 2; // OK, modify num's value</tt></pre> <p>To conclude, <tt>const_cast</tt> is used to remove the <tt>const</tt> or <tt>volatile</tt> qualities of an object. The resultant value can be used in a context that requires a non-<tt>const</tt> or <tt>volatile</tt> object. The cast operation is safe as long as the resultant value is not modified. It is possible to modify the value of the resultant object only if the original operand is not truly <tt>const</tt>.</p> <h4> reinterpret_cast</h4> <p><tt>reinterpret_cast <</tt><cite>to</cite><tt>> (</tt><cite>from</cite><tt>)</tt> is used in low-level, unsafe conversions. <tt>reinterpret_cast</tt> merely returns a low-level reinterpretation of the bit pattern of its operand. Note, however, that <tt>reinterpret_cast</tt> cannot alter the cv-qualification of its operand. The use of <tt>reinterpret_cast</tt> is dangerous and highly non-portable -- use it sparingly. Following are examples of <tt>reinterpret_cast</tt> uses. </p> <p><tt>reinterpret_cast</tt> can be used to convert two pointers of completely nonrelated types, as in</p> <pre><tt>#include <cstdio></tt><tt>void mem_probe()</tt><tt>{</tt><tt> long n = 1000000L; long *pl = &n;</tt><tt> unsigned char * pc = reinterpret_cast <unsigned char *> (pl);</tt><tt> printf("%d %d %d %d", pc[0], pc[1], pc[2], pc[3]); //memory dump</tt><tt>}</tt></pre> <p><tt>reinterpret_cast</tt> can cast integers to pointers, and vice versa. For example</p> <pre><tt>void *pv = reinterpret_cast<void *> (0x00fffd);</tt><tt> int ptr = reinterpret_cast<int> (pv);</tt></pre> <p><tt>reinterpret_cast</tt> can also be used for conversions between different types of function pointers. The result of using the resultant pointer to call a function with a nonmatching type is undefined.</p> <p>Do not use <tt>reinterpret_cast</tt> instead of <tt>static_cast</tt> -- the results might be undefined. For example, using <tt>reinterpret_cast</tt> to navigate through the class hierarchy of a multiply-inherited object is likely to yield the wrong result. Consider the following:</p> <pre><tt>class A</tt><tt>{</tt><tt>private:</tt><tt> int n;</tt><tt>};</tt><tt>class B</tt><tt>{</tt><tt>private:</tt><tt> char c;</tt><tt>};</tt><tt>class C: public A, public B</tt><tt>{};</tt><tt>void func(B * pb)</tt><tt>{</tt><tt> C *pc1 = static_cast<C*> (pb); //correct offset adjustment</tt><tt> C *pc2 = reinterpret_cast<C*> (pb); //no offset calculated</tt><tt>}</tt><tt>int main()</tt><tt>{</tt><tt> B b;</tt><tt> func(&b);</tt><tt>}</tt></pre> <p>On my machine, <tt>pc1</tt> is assigned the value <tt>0x0064fdf0</tt>, whereas <tt>pc2</tt> is assigned <tt>0x0064fdf4</tt>. This demonstrates the difference between the two cast operators. Using the information that is available at compile time, <tt>static_cast</tt> converts a pointer to <tt>B</tt> to a pointer to <tt>C</tt>. It does so by causing <tt>pc1</tt> to point at the start of <tt>C</tt> by subtracting the offset of the subobject <tt>B</tt>. On the other hand, <tt>reinterpret_cast</tt> simply assigns the binary value of <tt>pb</tt> to <tt>pc2</tt>, without any further adjustments; for this reason, it yields the wrong result.</p> <h4> dynamic_cast</h4> <p>In pre-standard C++, as was noted earlier, C-style cast was used to perform a dynamic cast as well. The cast was either static or dynamic, depending on the type of the operand. The Standardization committee, however, opposed this approach. An expensive runtime operation that looked exactly like a static cast (that is, penalty-free) can mislead the users. For this purpose, a new operator was introduced to the language: <tt>dynamic_cast</tt> (<tt>dynamic_cast</tt> is discussed in further detail in Chapter 7, "Runtime Type Identification"). The name and the syntax of <tt>dynamic_cast</tt> were chosen to look markedly different from C-style cast. All other new typecast operators follow this model. Following is an example of <tt>dynamic_cast</tt>:</p> <pre><tt>Derived *p = dynamic_cast<derived *> (&base); //pointer form</tt><tt>Derived & rd = dynamic_cast<derived &> (base); //reference form</tt></pre> <h4> Conclusions</h4> <p>The new typecasting operators are clearer and more explicit in their intended purpose. A name such as <tt>dynamic_cast</tt><i>,</i> for example<i>,</i> warns its users about its incurred runtime overhead. Most importantly, though, the new cast operators are safer because they give the compiler a chance to detect the programmer's mistakes.</p> <p>Users might find the proliferation of cast operators somewhat confusing. In particular, the choice between <tt>static_cast</tt> and <tt>reinterpret_cast</tt> might not seem immediately clear. How to choose? As a rule, <tt>static_cast</tt> is the first choice. If the compiler refuses to accept it, use <tt>reinterpret_cast</tt> instead.</p> <h3> <a name="Heading16">Built-in bool Type</a></h3> <p>The built-in <tt>bool</tt> data type was added to the Standard after consideration of several other proposals. None of these was found satisfactory. Following is an overview some of these proposals, which is in turn followed by a discussion of the characteristics of the <tt>bool</tt> type.</p> <h4> typedef Boolean</h4> <p>One suggestion was to use a <tt>typedef</tt> for a Boolean data type:</p> <pre><tt>typedef int bool;</tt></pre> <p>However, a <tt>typedef</tt> that relies on another built-in type of the language renders the Boolean type unusable with some language features. For example, using it in function overloading can result in ambiguities:</p> <pre><tt>void f(bool);</tt><tt>void f(int);//error, redefinition of void f(bool);</tt></pre> <p>In addition, a <tt>typedef</tt> is not strongly-typed. Consequently, it is impossible to ensure that only Boolean values are assigned to it in a context that requires Boolean values.</p> <h4> enum Type</h4> <p>An alternative solution was to use an <tt>enum</tt> type:</p> <pre><tt>enum bool { false, true};</tt></pre> <p><tt>enum</tt>s are strongly-typed. However, the committee wanted to ensure backward compatibility with old code that used <tt>int</tt> values as a Boolean data type. For example</p> <pre><tt>#include <ctype.h></tt><tt>enum bool {false, true};</tt><tt>void f()</tt><tt>{</tt><tt> enum bool b;</tt><tt> b = islower('a'); //compile time error, int assigned to an enum</tt><tt>}</tt></pre> <h4> Class bool</h4> <p>A third suggestion was to use a class such as the following:</p> <pre><tt>class bool</tt><tt>{</tt><tt>private:</tt><tt> int val;</tt><tt>public:</tt><tt> operator int();</tt><tt>};</tt></pre> <p>Such a class guarantees type uniqueness, so it can be used to overload functions and to specialize templates. In addition, it is backward compatible with Boolean integers. There are, however, several drawbacks to the class approach. First, users are required to <tt>#include</tt> a dedicated header and to link their programs with the compiled code of the class. Worse yet, the conversion operator might interfere with user-defined conversion operators that are defined in other classes. Finally, a full-blown class that defines constructors and conversion operators is significantly less efficient than a fundamental type. For these reasons, the Standardization committee decided to add a new built-in type.</p> <h4> A Built-in Type bool</h4> <p><tt>bool</tt> is an implementation-dependent integral type that can hold either a <tt>true</tt> or a <tt>false</tt> value. A standardized Boolean type has several advantages:</p> <ul> <li> <p> <b>Portability</b> -- All Standard compliant compilers support <tt>bool</tt> type. When code is ported to different platforms, it will work as expected.</p> </li> <p></p> <li> <b>Readability</b> -- The use of explicit keywords such as <tt>true</tt>, <tt>false</tt>, and <tt>bool</tt> is self-documenting and is more evident than the use of <tt>int</tt> values.</li> <p></p> <li> <b>Type Distinctness</b> -- Because <tt>bool</tt> is a distinct type, the following functions are now also distinct:</li> </ul> <p></p> <pre><tt>void f(bool b);</tt><tt>void f(int n);</tt></pre> <ul> <li> <b>Performance</b> -- Memory usage can be optimized by the implementation, which is allowed to use a single byte to represent a <tt>bool</tt> instead of an <tt>int</tt>. In addition, the use of a built-in type rather than a class also ensures the highest performance.</li> </ul> <p></p> <p>With the introduction of the <tt>bool</tt> data type, built-in operators were modified accordingly to work with <tt>bool</tt> values. The logical operators <tt>&&</tt>, <tt>||</tt>, and <tt>!</tt> now take <tt>bool</tt> values as arguments and return <tt>bool</tt> results. Similarly, the relational operators <tt><</tt>, <tt>></tt>, <tt><=</tt>, <tt>>=</tt>, <tt>==</tt>, and <tt>!=</tt> return <tt>bool</tt> results. In addition, <tt>iostream</tt> classes were adjusted to support the new type.</p> <p><b>(f)Viewing <tt>bool</tt> Variables as Literals</b></p> <p>By default, <tt>iostream</tt> objects display <tt>bool</tt> variables as <tt>0</tt> and <tt>1</tt>. It is possible to override the default setting by inserting the formatting flag <tt>boolalpha</tt> to the stream object. Subsequently, the symbolic representations <tt>false</tt> and <tt>true</tt> are displayed instead of <tt>0</tt> and <tt>1</tt>. For example</p> <pre><tt>#include <iostream></tt><tt>using namespace std;</tt><tt>int main()</tt><tt>{</tt><tt> bool b = true;</tt><tt> cout<<b; // default setting; display 1</tt><tt> cout<<boolalpha; //henceforth, display 'true' and 'false' instead of 1 and 0</tt><tt> cout<<b; // output: true</tt><tt> cout<<!b; // output: false</tt><tt> return 0;</tt><tt>}</tt></pre> <h3> <a name="Heading17">Exception Handling</a></h3> <p>Exception handling is used to report and handle runtime errors. Supplementary features, namely <i>exception specifications</i> and <i>function </i><tt>try</tt><i> blocks</i>, were added to the Standard in recent years. The following sections provide a brief overview of these features. (Exception handling and the supplementary features are discussed in more detail in Chapter 6, "Exception Handling.")</p> <h4> Exception Specification</h4> <p>A function can indicate the potential exceptions it can throw by specifying a list of these exceptions. Exception specifications are particularly useful when users of such a function can only view its prototype but cannot access its source file. Following is an example of specifying an exception:</p> <pre><tt>class Zerodivide{/*..*/};</tt><tt>int divide (int, int) throw(Zerodivide); //function may throw an exception</tt><tt> //of type Zerodivide, but no other</tt></pre> <h4> Function try Blocks</h4> <p>A <i>function </i><tt>try</tt><i> block</i> is a function whose body consists of a <tt>try</tt> block and its associated handlers. A function <tt>try</tt> block enables you to catch exceptions that might be thrown by a base class constructor or by a constructor of a member object. The original specification of exception handling did not enable users to handle exceptions thrown from a constructor or a member initialization list locally; a function <tt>try</tt>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -