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

📄 ch07.htm

📁 c++语言操作手册
💻 HTM
📖 第 1 页 / 共 3 页
字号:
<tt>}</tt></pre><p>In general, all instances of the same type share a single <tt>type_info</tt>   object. The most widely used member functions of <tt>type_info</tt> are <tt>name()</tt>   and <tt>operator==</tt>. But before you can invoke these member functions, you   have to access the <tt>type_info</tt> object itself. How is it done?</p><h3> <a name="Heading8">Operator typeid</a></h3><p>Operator <tt>typeid</tt> takes either an object or a type name as its argument   and returns a matching <tt>const type_info</tt> object. The dynamic type of   an object can be examined as follows:</p><pre><tt>OnRightClick (File &amp; file)  </tt><tt>{</tt><tt>  if ( <b>typeid( file)  == typeid( TextFile ) </b>)</tt><tt>  {</tt><tt>    //received a TextFile object; printing should be enabled</tt><tt>  }</tt><tt>  else</tt><tt>  {</tt><tt>    //not a TextFile object, printing disabled</tt><tt>  }</tt><tt>}</tt></pre><p>To understand how it works, look at the highlighted source line:</p><pre><tt>if ( typeid( file)  == typeid( TextFile ) ).</tt></pre><p>The <tt>if</tt> statement tests whether the dynamic type of the argument <tt>file</tt>   is <tt>TextFile</tt> (the static type of <tt>file</tt> is <tt>File</tt>, of   course). The leftmost expression, <tt>typeid(file)</tt>, returns a <tt>type_info   </tt>object that holds the necessary runtime type information that is associated   with the object <tt>file</tt>. The rightmost expression, <tt>typeid(TextFile)</tt>,   returns the type information that is associated with class <tt>TextFile</tt>.   When <tt>typeid</tt> is applied to a class name rather than an object, it always   returns a <tt>type_info</tt> object that corresponds to that class name. As   you saw earlier, <tt>type_info</tt> overloads the operator <tt>==</tt>. Therefore,   the <tt>type_info</tt> object that is returned by the leftmost <tt>typeid</tt>   expression is compared to the <tt>type_info</tt> object that is returned by   the rightmost <tt>typeid</tt> expression. If indeed <tt>file</tt> is an instance   of <tt>TextFile</tt>, the <tt>if</tt> statement evaluates to <tt>true</tt>.   In this case, <tt>OnRightClick</tt> displays an additional option in the menu   -- <tt>print()</tt>. If, on the other hand, <tt>file</tt> is not a <tt>TextFile</tt>,   the <tt>if</tt> statement evaluates to <tt>false</tt>, and the <tt>print()</tt>   option is disabled. This is all well and good, but a <tt>typeid</tt>-based solution   has a drawback. Suppose that you want to add support for a new type of files,   for example HTML files. What happens when the file manager application has to   be extended? HTML files are essentially text files. They can be read and printed.   However, they differ from plain text files in some respects. An <tt>open</tt>   message applied to an HTML file launches a browser rather than a word processor.   In addition, HTML files have to be converted to a printable format before they   can be printed. The need to extend a system's functionality at a minimal cost   is a challenge that is faced by software developers every day. Object-oriented   programming and design can facilitate the task. By subclassing <tt>TextFile</tt>,   you can reuse its existing behavior and implement only the additional functionality   that is required for HTML files:</p><pre><tt>class HTMLFile : public TextFile</tt><tt>{</tt><tt>  void open () { Launch_Browser (); }  </tt><tt>  void virtual print();  // perform the necessary conversions to a </tt><tt>                         //printable format and then print file</tt><tt>};</tt></pre><p>This is, however, only half of the story. <tt>OnRightClick()</tt> fails badly   when it receives an object of type <tt>HTMLFile</tt>. Look at it again to see   why:</p><pre><tt>OnRightClick (File &amp; file) //operating system's API function</tt><tt>{</tt><tt>  if ( <b>typeid( file)  == typeid( TextFile ) </b>)</tt><tt>  {</tt><tt>    //we received a TextFile object; printing should be enabled</tt><tt>  }</tt><tt>  else //OOPS! we get here when file is of type HTMLFile</tt><tt>  {</tt><tt>  }</tt><tt>}</tt></pre><p><tt>typeid</tt> returns the exact type information of its argument. Therefore,   the <tt>if</tt> statement in <tt>OnRightClick()</tt> evaluates to <tt>false</tt>   when the argument is an <tt>HTMLFile</tt>. But a <tt>false</tt> value implies   a binary file! Consequently, printing is disabled. This onerous bug is likely   to occur every time you add support for a new file type. Of course, you can   modify <tt>OnRightClick()</tt> so that it performs another test:</p><pre><tt>OnRightClick (File &amp; file) //operating system's API function</tt><tt>{</tt><tt>  if ( (<b>typeid( file)  == typeid( TextFile ))  </b></tt><tt><b>    || (typeid( file)  == typeid( HTMLFile)) </b>) //check for HTMLFile as well</tt><tt>  {</tt><tt>    //we received either a TextFile or an HTMLFile; printing should be enabled</tt><tt>  }</tt><tt>  else //it's a binary file, no print option</tt><tt>  {</tt><tt>  }</tt><tt>}</tt></pre><p>However, this solution is cumbersome and error prone. Furthermore, it imposes   an unacceptable burden on the programmers who maintain this function. Not only   are they required to clutter up <tt>OnRightClick()</tt> with additional code   every time a new class is derived from <tt>File</tt>, but they also have to   be on guard to detect any new class that has been derived from <tt>File</tt>   lately. Fortunately, C++ offers a much better way to handle this situation.</p><blockquote>  <hr>  <strong>NOTE: </strong> You can use <tt>typeid</tt> to retrieve the type information   of non-polymorphic objects and fundamental types. However, the result refers   to a <tt>type_info</tt> object that represents the static type of the operand.   For example   <hr></blockquote><pre><tt>#include&lt;typeinfo&gt;</tt><tt>#include &lt;iostream&gt;</tt><tt>#include &lt;string&gt;</tt><tt>using namespace std;</tt><tt>typedef int I;</tt><tt>void fundamental()</tt><tt>{</tt><tt>  cout&lt;&lt;typeid(I).name()&lt;&lt;endl; //display 'int'</tt><tt>}</tt><tt>void non_polymorphic()</tt><tt>{</tt><tt>  cout&lt;&lt;typeid(string).name()&lt;&lt;endl;</tt><tt>}</tt></pre><blockquote>  <hr>  <strong>NOTE: </strong> Note however, that applying <tt>dynamic_cast</tt> to   fundamental types or non-polymorphic classes is a compile time error.   <hr></blockquote><p></p><h3> <a name="Heading9">Operator dynamic_cast&lt;&gt;</a></h3><p>It is a mistake to allow <tt>OnRightClick()</tt> to take care of every conceivable   class type. In doing so, you are forced to modify <tt>OnRightClick()</tt> any   time you add a new file class or modify an existing class. In software design,   and in object-oriented design in particular, you want to minimize such dependencies.   If you examine <tt>OnRightClick()</tt> closely, you can see that it doesn't   really know whether its argument is an instance of class <tt>TextFile</tt> (or   of any other class, for that matter). Rather, all it needs to know is whether   its argument is a <tt>TextFile</tt>. There is a big difference between the two   -- an object <i>is-a</i> <tt>TextFile</tt> if it is an instance of class <tt>TextFile</tt>   or if it is an instance of any class derived from <tt>TextFile</tt>. However,   <tt>typeid</tt> is incapable of examining the derivation hierarchy of an object.   For this purpose, you have to use the operator <tt>dynamic_cast&lt;&gt;</tt>.   <tt>dynamic_cast&lt;&gt;</tt> takes two arguments: The first is a type name,   and the second argument is an object, which <tt>dynamic_cast&lt;&gt;</tt> attempts   to cast at runtime to the desired type. For example</p><pre><tt>dynamic_cast &lt;TextFile &amp;&gt; (file); //attempt to cast file to a reference to </tt><tt>                                  //an object of type TextFile</tt></pre><p>If the attempted cast succeeds, either the second argument is an instance of   the class name that appears as the second argument or it is an object derived   from it. The preceding <tt>dynamic_cast&lt;&gt;</tt> expression succeeds if   <tt>file</tt> <i>is-a</i> <tt>TextFile</tt>. This is exactly the information   needed by <tt>OnRightClick</tt> to operate properly. But how do you know whether   <tt>dynamic_cast&lt;&gt;</tt> was successful?</p><h4> Pointer Cast and Reference Cast</h4><p>There are two flavors of <tt>dynamic_cast&lt;&gt;</tt>. One uses pointers and   the other uses references. Accordingly, <tt>dynamic_cast&lt;&gt;</tt> returns   a pointer or a reference of the desired type when it succeeds. When <tt>dynamic_cast&lt;&gt;</tt>   cannot perform the cast, it returns a <tt>NULL</tt> pointer or, in the case   of a reference, it throws an exception of type <tt>std::bad_cast.</tt> Look   at the following pointer cast example:</p><pre><tt>TextFile * pTest = dynamic_cast &lt; TextFile *&gt; (&amp;file); //attempt to cast </tt><tt>                                                       //file address to a pointer to TextFile</tt><tt>if (pTest) //dynamic_cast succeeded, file is-a TextFile</tt><tt>{</tt><tt>  //use pTest</tt><tt>}</tt><tt>else // file is not a TextFile;  pTest has a NULL value</tt><tt>{</tt><tt>}</tt></pre><p>C++ does not have <tt>NULL</tt> references. Therefore, when a reference <tt>dynamic_cast&lt;&gt;</tt>   fails, it throws an exception of type <tt>std::bad_cast</tt>. That is why you   always need to place a reference <tt>dynamic_cast&lt;&gt;</tt> expression within   a <tt>try</tt>-block and include a suitable <tt>catch</tt>-statement to handle   <tt>std::bad_cast</tt> exceptions (see also Chapter 6, "Exception Handling").   For example</p><pre><tt>try</tt><tt>{</tt><tt>  TextFile  tf = dynamic_cast &lt; TextFile &amp;&gt; (file); </tt><tt>  //use tf safely,</tt><tt>}</tt><tt>catch (std::bad_cast)</tt><tt>{ </tt><tt>  //dynamic_cast&lt;&gt; failed</tt><tt>}</tt></pre><p>Now you can revise <tt>OnRightClick()</tt> to handle <tt>HTMLFile</tt> objects   properly:</p><pre><tt>OnRightClick (File &amp; file)  </tt><tt>{</tt><tt>  try</tt><tt>  {</tt><tt>    TextFile temp = dynamic_cast&lt;TextFile&amp;&gt; (file);</tt><tt>    //display options, including "print"</tt><tt>    switch (message)</tt><tt>    {</tt><tt>    case m_open:</tt><tt>      temp.open();  //either TextFile::open or HTMLFile::open </tt><tt>    break;</tt><tt>    case m_print:</tt><tt>      temp.print();//either TextFile::print or HTMLFile::print</tt><tt>    break;</tt><tt>    }//switch</tt><tt>  }//try</tt><tt>  catch (std::bad_cast&amp; noTextFile)</tt><tt>  {</tt><tt>    // treat file as a BinaryFile; exclude"print"</tt><tt>  }</tt><tt>}// OnRightClick</tt></pre><p>The revised version of <tt>OnRightClick()</tt> handles an object of type <tt>HTMLFile</tt>   appropriately because an object of type <tt>HTMLFile</tt> <i>is-a</i> <tt>TextFile</tt>.   When the user clicks on the open message in the file manager application, the   function <tt>OnRightClick()</tt> invokes the member function <tt>open()</tt>   of its argument, which behaves as expected because it was overridden in class   <tt>HTMLFile</tt>. Likewise, when <tt>OnRightClick()</tt> detects that its argument   is a TextFile, it displays a print option. If the user clicks on this option,   <tt>OnRightClick()</tt> sends the message <tt>print</tt> to its argument, which   reacts as expected.</p><h3> <a name="Heading10">Other Uses of dynamic_cast&lt;&gt;</a></h3>

⌨️ 快捷键说明

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