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

📄 index.html

📁 C程序员手册(英文)
💻 HTML
📖 第 1 页 / 共 3 页
字号:
  reacts as expected.</p>
<h3> <a name="Heading10">Other Uses of dynamic_cast&lt;&gt;</a></h3>
<p>Dynamic type casts are required in cases in which the dynamic type of an object 
  -- rather than its static type -- is necessary to perform the cast properly. 
  Note that any attempt to use a static cast in these cases is either flagged 
  as an error by the compiler, or -- even worse -- it might result in undefined 
  behavior at runtime.</p>
<h4> Cross casts</h4>
<p>A <i>cross cast</i> converts a multiply-inherited object to one of its secondary 
  base classes. To demonstrate what a cross cast does, consider the following 
  class hierarchy:</p>
<pre>
<tt>struct A</tt>
<tt>{</tt>
<tt>  int i;</tt>
<tt>  virtual ~A () {} //enforce polymorphism; needed for dynamic_cast</tt>
<tt>};</tt>
<tt>struct B</tt>
<tt>{</tt>
<tt>  bool b;</tt>
<tt>};</tt>
<tt>struct D: public A, public B</tt>
<tt>{</tt>
<tt>  int k;</tt>
<tt>  D() { b = true; i = k = 0; } </tt>
<tt>};</tt>
<tt>A *pa = new D;</tt>
<tt>B *pb = dynamic_cast&lt;B*&gt; pa;  //cross cast; access the second base </tt>
<tt>                              //of a multiply-derived object</tt>
</pre>
<p>The static type of <tt>pa</tt> is "pointer to <tt>A</tt>", whereas its dynamic 
  type is "pointer to <tt>D</tt>". A simple <tt>static_cast&lt;&gt;</tt> cannot 
  convert a pointer to <tt>A</tt> into a pointer to <tt>B</tt> because <tt>A</tt> 
  and <tt>B</tt> are unrelated (your compiler issues an error message in this 
  case). A brute force cast, (for example <tt>reinterpret_cast&lt;&gt;</tt> or 
  C-style cast), has disastrous results at runtime because the compiler simply 
  assigns <tt>pa</tt> to <tt>pb</tt>. However, the <tt>B</tt> subobject is located 
  at a different address within <tt>D</tt> than the <tt>A</tt> subobject. To perform 
  the cross cast properly, the value of <tt>pb</tt> has to be calculated at runtime. 
  After all, the cross cast can be done in a translation unit that doesn't even 
  know that class <tt>D</tt> exists! The following program demonstrates why a 
  dynamic cast, rather than compile-time cast, is required:</p>
<pre>
<tt>int main()</tt>
<tt>{</tt>
<tt>  A *pa = new D;</tt>
<tt>  B *pb = (B*) pa;  // disastrous; pb points to the subobject A within d</tt>
<tt>  bool bb = pb-&gt;b;  // bb has an undefined value</tt>
<tt>  cout&lt;&lt; "pa: " &lt;&lt; pa &lt;&lt; " pb: "&lt;&lt;pb &lt;&lt;endl;  // pb was not properly </tt>
<tt>                                               //adjusted; pa and pb are identical</tt>
<tt>  pb = dynamic_cast&lt;B*&gt; (pa); //cross cast; adjust pb correctly</tt>
<tt>  bb= pb-&gt;b; //OK, bb is true</tt>
<tt>  cout&lt;&lt; "pa: "&lt;&lt; pa &lt;&lt; " pb: " &lt;&lt; pb &lt;&lt;endl; // OK, pb was properly adjusted;</tt>
<tt>                                             // pa and pb have distinct values</tt>
<tt>  return 0;</tt>
<tt>}</tt>
</pre>
<p>The program displays two lines of output; the first shows that the memory addresses 
  of <tt>pa</tt> and <tt>pb</tt> are identical. The second line shows that the 
  memory addresses of <tt>pa</tt> and <tt>pb</tt> are different after performing 
  a dynamic cast as required.</p>
<h4> Downcasting From a Virtual Base</h4>
<p>A <i>downcast</i> is a cast from a base to a derived object. Before the introduction 
  of RTTI to the language, downcasts were regarded as a bad programming practice. 
  They were unsafe, and some even viewed the reliance on the dynamic type of an 
  object a violation of object-oriented principles (see also Chapter 2, "Standard 
  Briefing: the Latest Addenda to ANSI/ISO C++"). <tt>dynamic_cast&lt;&gt;</tt> 
  enables you to use safe, standardized, and simple downcasts from a virtual base 
  to its derived object. Look at the following example:</p>
<pre>
<tt> struct V</tt>
<tt>{</tt>
<tt>  virtual ~V (){} //ensure polymorphism</tt>
<tt>};</tt>
<tt>struct A: virtual V {};</tt>
<tt>struct B: virtual V {};</tt>
<tt>struct D: A, B {};</tt>
<tt>#include &lt;iostream&gt;</tt>
<tt>using namespace std;</tt>
<tt>int main()</tt>
<tt>{</tt>
<tt> V *pv = new D;</tt>
<tt> A* pa = dynamic_cast&lt;A*&gt; (pv); // downcast</tt>
<tt> cout&lt;&lt; "pv: "&lt;&lt; pv &lt;&lt; " pa: " &lt;&lt; pa &lt;&lt;endl; // OK, pv and pa have </tt>
<tt>                                             //different addresses</tt>
<tt> return 0;</tt>
<tt>}</tt>
</pre>
<p><tt>V</tt> is a virtual base for classes <tt>A</tt> and <tt>B</tt>. <tt>D</tt> 
  is multiply-inherited from <tt>A</tt> and <tt>B</tt>. Inside <tt>main()</tt>, 
  <tt>pv</tt> is declared as a "pointer to <tt>V</tt>" and its dynamic type is 
  "pointer to <tt>D</tt>". Here again, as in the cross cast example, the dynamic 
  type of <tt>pv</tt> is needed in order to properly downcast it to a pointer 
  to <tt>A</tt>. A <tt>static_cast&lt;&gt;</tt> would be rejected by the compiler. 
  As you read in Chapter 5, the memory layout of a virtual subobject might be 
  different from that of a nonvirtual subobject. Consequently, it is impossible 
  to calculate at compile time the address of the subobject <tt>A</tt> within 
  the object pointed to by <tt>pv</tt>. As the output of the program shows, <tt>pv</tt> 
  and <tt>pa</tt> indeed point to different memory addresses.</p>
<h2> <a name="Heading11">The Cost of Runtime Type Information</a></h2>
<p>Runtime Type Information is not free. To estimate how expensive it is in terms 
  of performance, it is important to understand how it is implemented behind the 
  scenes. Some of the technical details are platform-dependent. Still, the basic 
  model that is presented here can give you a fair idea of the performance penalties 
  of RTTI in terms of memory overhead and execution speed.</p>
<h3> <a name="Heading12">Memory Overhead</a></h3>
<p>Additional memory is needed to store the <tt>type_info</tt> object of every 
  fundamental and user-defined type. Ideally, the implementation associates a 
  single <tt>type_info</tt> object with every distinct type. However, this is 
  not a requirement, and under some circumstances -- for example, dynamically 
  linked libraries -- it is impossible to guarantee that only one <tt>type_info</tt> 
  object per class exists. . Therefore, an implementation can create more than 
  one <tt>type_info</tt> object per type.</p>
<p>As was previously noted, there is a practical reason that <tt>dynamic_cast&lt;&gt;</tt> 
  is applicable only to polymorphic objects: An object does not store its runtime 
  type information directly (as a data member, for example). </p>
<h3> <a name="Heading13">Runtime Type Information of Polymorphic Objects</a></h3>
<p>Every polymorphic object has a pointer to its virtual functions table. This 
  pointer, traditionally named <tt>vptr,</tt> holds the address of a dispatch 
  table that contains the memory addresses of every virtual function in this class. 
  The trick is to add another entry to this table. This entry points at the class's 
  <tt>type_info</tt> object. In other words, the <tt>vptr</tt> data member of 
  a polymorphic object points at a table of pointers, in which the address of 
  <tt>type_info</tt> is kept at a fixed position. This model is very economical 
  in terms of memory usage; it requires a single <tt>type_info</tt> object and 
  a pointer for every polymorphic class. Note that this is a fixed cost, regardless 
  of how many instances of the class actually exist in the program. The cost of 
  retrieving an object's runtime type information is therefore a single pointer 
  indirection, which might be less efficient than direct access to a data member; 
  still, though, it is equivalent to a virtual function invocation.</p>
<h3> <a name="Heading14">Additional Overhead</a></h3>
<p>A pointer indirection, a <tt>type_info</tt> object, and a pointer per class 
  sound like a reasonable price to pay for RTTI support. This is not the full 
  picture, however. The <tt>type_info</tt> objects, just like any other object, 
  have to be constructed. Large programs that contain hundreds of distinct polymorphic 
  classes have to construct an equivalent number of <tt>type_info</tt> objects 
  as well.</p>
<h3> <a name="Heading15">RTTI Support Can Usually Be Toggled</a></h3>
<p>This overhead is imposed even if you never use RTTI in your programs. For this 
  reason, most compilers enable you to switch off their RTTI support (check the 
  user's manual to see the default RTTI setting of your compiler and how it can 
  be modified). If you never use RTTI in your programs, iyou can turn off your 
  compiler's RTTI support. The results are smaller executables and a slightly 
  faster code.</p>
<h3> <a name="Heading16">typeid Versus dynamic_cast&lt;&gt;</a></h3>
<p>Until now, this chapter has discussed the indirect cost of RTTI support. It 
  is now time to explore the cost of its direct usage -- that is, applying <tt>typeid</tt> 
  and <tt>dynamic_cast&lt;&gt;</tt>.</p>
<p>A <tt>typeid</tt> invocation is a constant time operation. It takes the same 
  length of time to retrieve the runtime type information of every polymorphic 
  object, regardless of its derivational complexity. In essence, calling <tt>typeid</tt> 
  is similar to invoking a virtual member function. For instance, the expression 
  <tt>typeid(obj)</tt> is evaluated into something similar to the following:</p>
<pre>
<tt>return *(obj-&gt;__vptr[0]); //return the type_info object whose address</tt>
<tt>                         // is stored at offset 0 in the virtual table of obj</tt>
</pre>
<p>Note that the pointer to a class's <tt>type_info</tt> object is stored at a 
  fixed offset in the virtual table (usually <tt>0, but this is implementation-dependent</tt>).</p>
<p>Unlike <tt>typeid</tt>, <tt>dynamic_cast&lt;&gt;</tt> is not a constant time 
  operation. In the expression <tt>dynamic_cast&lt;T&amp;&gt; (obj)</tt>, where 
  <tt>T</tt> is the target type and <tt>obj</tt> is the operand, the time that 
  is needed to cast the operand to the target type depends on the complexity of 
  the class hierarchy of <tt>obj</tt>. <tt>dynamic_cast&lt;&gt;</tt> has to traverse 
  the derivation tree of the <tt>obj</tt> until it has located the target object 
  in it. When the target is a virtual base, the dynamic cast becomes even more 
  complicated (albeit unavoidable, as you have seen); consequently, it takes longer 
  to execute. The worst case scenario is when the operand is a deeply derived 
  object and the target is a nonrelated class type. In this case, <tt>dynamic_cast&lt;&gt;</tt> 
  has to traverse the entire derivation tree of <tt>obj</tt> before it can confidently 
  decide that <tt>obj</tt> cannot be cast to a <tt>T</tt>. In other words, a failed 
  <tt>dynamic_cast&lt;&gt;</tt> is an O(<tt>n</tt>) operation, where <tt>n</tt> 
  is the number of base classes of the operand.</p>
<p>You might recall the conclusion that from a design point of view, <tt>dynamic_cast&lt;&gt;</tt> 
  is preferable to <tt>typeid</tt> because the former enables more flexibility 
  and extensibility. Notwithstanding that, the runtime overhead of <tt>typeid</tt> 
  can be less expensive than <tt>dynamic_cast&lt;&gt;</tt>, depending on the derivational 
  complexity of the entities involved.</p>
<h2> <a name="Heading17">Conclusions</a></h2>
<p>The RTTI mechanism of C++ consists of three components: operator <tt>typeid</tt>, 
  operator <tt>dynamic_cast&lt;&gt;</tt>, and class <tt>std::type_info</tt>. RTTI 
  is relatively new in C++. Some existing compilers do not support it yet. Furthermore, 
  compilers that support it can usually be configured to disable RTTI support. 
  Even when there is no explicit usage of RTTI in a program, the compiler automatically 
  adds the necessary "scaffolding" to the resultant executable. To avert this, 
  you can usually switch off your compiler's RTTI support.</p>
<p>From the object-oriented design point of view, operator <tt>dynamic_cast&lt;&gt;</tt> 
  is preferable to <tt>typeid</tt> because it enables more flexibility and robustness, 
  as you have seen. However, <tt>dynamic_cast&lt;&gt;</tt> can be slower than 
  <tt>typeid</tt> because its performance depends on the proximity of its target 
  and operand, as well as on the derivational complexity of the latter. When complex 
  derivational hierarchies are used, the incurred performance penalty might be 
  noticeable. It is recommended, therefore, that you use RTTI judiciously. In 
  many cases, a virtual member function is sufficient to achieve the necessary 
  polymorphic behavior. Only when virtual member functions are insufficient should 
  RTTI be considered.</p>
<p>Following are a few additional notes to keep in mind when using RTTI:</p>
<ul>
  <li>In order to enable RTTI support, an object must have at least one virtual 
    member function. In addition, switch on your compiler's RTTI support (please 
    consult your user's manual for further information) if it isn't already on.</li>
  <p></p>
  <li> Make sure that your program has a <tt>catch</tt>-statement to handle <tt>std::bad_cast</tt> 
    exceptions whenever you are using <tt>dynamic_cast&lt;&gt;</tt> with a reference. 
    Note also that an attempt to dereference a null pointer in a <tt>typeid</tt> 
    expression, as in <tt>typeid(*p)</tt> where <tt>p</tt> is <tt>NULL</tt>, results 
    in a <tt>std::bad_typeid</tt> exception being thrown.</li>
  <p></p>
  <li> When you are using <tt>dynamic_cast&lt;&gt; </tt>with a pointer, always 
    check the returned value.</li>
</ul>
<CENTER>
<P>
<HR>
  <A HREF="/publishers/que/series/professional/0789720221/index.htm"><img src="/publishers/que/series/professional/0789720221/button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR>
<BR>
<BR>
<p></P>

<P>&#169; <A HREF="/publishers/que/series/professional/0789720221/copy.htm">Copyright 1999</A>, Macmillan Computer Publishing. All
rights reserved.</p>
</CENTER>


</BODY>

</HTML>

⌨️ 快捷键说明

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