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

📄 chap09.htm

📁 This is the second part of that lab manual to teach you how to make real-time programme and how to d
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source: 
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:C:\TEMP\TicV2\html\TicV2.rtf
Application Directory:C:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:09/26/2001
Translation Time:08:32:34
Translation Platform:Win32
Number of Output files:19
This File:C:\TEMP\TicV2\html\Chap09.htm
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>9: Run-time type identification</TITLE>
</HEAD>

<BODY  BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
  <a href="http://www.MindView.net">
  <img src="mindview.gif" alt="MindView Inc." BORDER = "0"></a>
  <CENTER>
    <FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
    [ <a href="README.txt">Viewing Hints</a> ]
    [ <a href="RevisionHistory.htm">Revision History</a> ]
    [ <a href="http://www.mindview.net/Books/TICPP/ThinkingInCPP2e.html">Book Home Page</a> ]
    [ <a href="http://www.mindview.net/Etc/MailingList.html">Free Newsletter</a> ] <br>
    [ <a href="http://www.mindview.net/Seminars">Seminars</a> ]
    [ <a href="http://www.mindview.net/CDs">Seminars on CD ROM</a> ]
    [ <a href="http://www.mindview.net/Services">Consulting</a> ]
    </FONT>
  <H2><FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans">
  Thinking in C++, 2nd edition, Volume 2<br>
  <small>Revision 4.0</small></FONT></H2>
  <H3><FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans">
  by Bruce Eckel &amp; Chuck Allison<br>&copy;2001 MindView, Inc.</FONT></H3>
  
    <FONT FACE="Verdana, Tahoma, Arial, Helvetica, Sans" size = "-1">
     [ <a href="Part3.htm">Previous Chapter</a> ] 
    
    [ <a href="SimpCont.htm">Short TOC</a> ] 
    [ <a href="Contents.htm">Table of Contents</a> ] 
  
        [ <a href="DocIdx.htm">Index</a> ]
        
     [ <a href="Chap10.htm">Next Chapter</a> ] 
    </FONT>
    
  </CENTER>
  </P></DIV><A NAME="_Toc305593307"></A><A NAME="_Toc305628779"></A><A NAME="_Toc312374132"></A><A NAME="_Toc519042080"></A><A NAME="Heading287"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H1 ALIGN="LEFT">
9: Run-time type identification</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana" SIZE=4>Run-time type identification
(RTTI) lets you find the exact type of an object when you have only a pointer or
reference to the base type. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><A NAME="Index501"></A><A NAME="Index502"></A><FONT FACE="Georgia">This
can be thought of as a &#8220;secondary&#8221; feature in C++, a pragmatism to
help out when you get into messy situations. Normally, you&#8217;ll want to
intentionally ignore the exact type of an object and let the virtual function
mechanism implement the correct behavior for that type. But occasionally
it&#8217;s useful to know the exact type of an object for which you only have a
base pointer. Often this information allows you to perform a special-case
operation more efficiently or prevent a base-class interface from becoming
ungainly. It happens enough that most class libraries contain virtual functions
to produce run-time type information. When exception handling was added to C++,
it required the exact type information about objects. It became an easy next
step to build access to that information into the language.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This chapter explains what RTTI is for
and how to use it. In addition, it explains the why and how of the new C++ cast
syntax, which has the same appearance as
RTTI.</FONT><A NAME="_Toc305593308"></A><A NAME="_Toc305628780"></A><A NAME="_Toc312374133"></A><A NAME="_Toc519042081"></A><BR></P></DIV>
<A NAME="Heading288"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
The &#8220;Shape&#8221; example</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is an example of a class hierarchy
that uses polymorphism. The generic type is the base class
<B>Shape<A NAME="Index503"></A><A NAME="Index504"></A></B>, and the specific
derived types are <B>Circle</B>, <B>Square</B>, and
<B>Triangle</B>:</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><IMG SRC="TicV201.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is a typical class-hierarchy
diagram, with the base class at the top and the derived classes growing
downward. The normal goal in object-oriented
programming<A NAME="Index505"></A><A NAME="Index506"></A> is for the bulk of
your code to manipulate pointers to the base type (<B>Shape</B>, in this case)
so if you decide to extend the program by adding a new class (<B>rhomboid</B>,
derived from <B>Shape</B>, for example), the bulk of the code is not affected.
In this example, the virtual function in the <B>Shape</B> interface is
<B>draw(&#160;)</B>, so the intent is for the client programmer to call
<B>draw(&#160;)</B> through a generic <B>Shape</B> pointer. <B>draw(&#160;)</B>
is redefined in all the derived classes, and because it is a virtual function,
the proper behavior will occur even though it is called through a generic
<B>Shape</B> pointer.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Thus, you generally create a specific
object (<B>Circle</B>, <B>Square</B>, or <B>Triangle</B>), take its address and
cast it to a <B>Shape*</B> (forgetting the specific type of the object), and use
that anonymous pointer in the rest of the program. Historically, diagrams are
drawn as seen above, so the act of casting from a more derived type to a base
type is called
<I>upcasting<A NAME="Index507"></A><A NAME="Index508"></A></I>.</FONT><A NAME="_Toc305593309"></A><A NAME="_Toc305628781"></A><A NAME="_Toc312374134"></A><A NAME="_Toc519042082"></A><BR></P></DIV>
<A NAME="Heading289"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
What is RTTI?</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But what if you have a special
programming problem that&#8217;s easiest to solve if you know the exact type of
a generic pointer<A NAME="Index509"></A><A NAME="Index510"></A>? For example,
suppose you want to allow your users to highlight all the shapes of any
particular type by turning them purple. This way, they can find all the
triangles on the screen by highlighting them. Your natural first approach may be
to try a virtual function like <B>TurnColorIfYouAreA(&#160;)</B>, which allows
enumerated arguments of some type <B>color</B> and of <B>Shape::Circle</B>,
<B>Shape::Square</B>, or <B>Shape::Triangle</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To solve this sort of problem, most class
library designers put virtual functions in the base class to return type
information about the specific object at runtime. You may have seen library
member functions with names like <B>isA(&#160;)</B> and <B>typeOf(&#160;)</B>.
These are vendor-defined<A NAME="Index511"></A><A NAME="Index512"></A> RTTI
functions. Using these functions, as you go through the list you can say,
&#8220;If you&#8217;re a triangle, turn purple.&#8221;</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When exception
handling<A NAME="Index513"></A><A NAME="Index514"></A> was added to C++, the
implementation required that some run-time type information be put into the
virtual function tables. This meant that with a small language extension the
programmer could also get the run-time type information about an object. All
library vendors were adding their own RTTI <A NAME="Index515"></A>anyway, so it
was included in the language.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">RTTI, like exceptions, depends on type
information residing in the virtual function table. If you try to use RTTI on a
class that has no virtual
functions<A NAME="Index516"></A><A NAME="Index517"></A><A NAME="Index518"></A>,
you&#8217;ll get unexpected results.
</FONT><A NAME="_Toc312374135"></A><A NAME="_Toc519042083"></A><BR></P></DIV>
<A NAME="Heading290"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Two syntaxes for RTTI</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are two different ways to use RTTI.
The first acts like <B>sizeof(&#160;)</B> because it looks like a function, but
it&#8217;s actually implemented by the compiler. <B>typeid(&#160;)
<A NAME="Index519"></A><A NAME="Index520"></A></B>takes an argument that&#8217;s
an object, a reference, or a pointer and returns a reference to a global<B>
const</B> object of type
<B>typeinfo<A NAME="Index521"></A><A NAME="Index522"></A></B>. These can be
compared to each other with the <B>operator==</B> and <B>operator!=</B>, and you
can also ask for the <B>name(&#160;)</B> of the type, which returns a string
representation of the type name. Note that if you hand <B>typeid(&#160;) </B>a
<B>Shape*</B>, it will say that the type is <B>Shape*</B>, so if you want to
know the exact type it is pointing to, you must dereference the pointer. For
example, if <B>s</B> is a <B>Shape*</B>,</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>cout &lt;&lt; <font color=#0000ff>typeid</font>(*s).name() &lt;&lt; endl;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">will print out the type of the object
<B>s</B> points to. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can also ask a <B>typeinfo</B> object
if it precedes another <B>typeinfo</B> object in the implementation-defined
&#8220;collation sequence,&#8221; using
<B>before(typeinfo&amp;)<A NAME="Index523"></A><A NAME="Index524"></A></B>,
which returns true or false. When you say,</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>if</font>(<font color=#0000ff>typeid</font>(me).before(<font color=#0000ff>typeid</font>(you))) <font color=#009900>// ...</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">you&#8217;re asking if <B>me</B> occurs
before <B>you</B> in the collation sequence.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The second syntax for RTTI is called a
&#8220;type-safe
downcast<A NAME="Index525"></A><A NAME="Index526"></A><A NAME="Index527"></A>.&#8221;
The reason for the term &#8220;downcast&#8221; is (again) the historical
arrangement of the class hierarchy diagram. If casting a <B>Circle*</B> to a
<B>Shape*</B> is an upcast, then casting a <B>Shape*</B> to a <B>Circle*</B> is
a downcast. However, you know a <B>Circle*</B> is also a <B>Shape*</B>,and the
compiler freely allows an upcast assignment, but you <I>don&#8217;t</I> know
that a <B>Shape*</B> is necessarily a <B>Circle*</B>, so the compiler
doesn&#8217;t allow you to perform a downcast assignment without using an
explicit cast. You can of course force your way through using ordinary C-style
casts or a C++ <B>static_cast</B> (described at the end of this chapter), which
says, &#8220;I hope this is actually a <B>Circle*</B>, and I&#8217;m going to
pretend it is.&#8221; Without some explicit knowledge that it <I>is</I> in fact
a <B>Circle</B>, this is a totally dangerous thing to do. A common approach in
vendor-defined RTTI is to create some function that attempts to assign (for this
example) a <B>Shape*</B> to a <B>Circle*</B>, checking the type in the process.
If this function returns the address, it was successful; if it returns null, you
didn&#8217;t have a <B>Circle*</B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The C++ RTTI typesafe-downcast follows
this &#8220;attempt-to-cast&#8221; function form, but it uses (very logically)
the template syntax to produce the special function
<B>dynamic_cast<A NAME="Index528"></A><A NAME="Index529"></A></B>. So the
example becomes</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Shape* sp = <font color=#0000ff>new</font> Circle;
Circle* cp = <font color=#0000ff>dynamic_cast</font>&lt;Circle*&gt;(sp);
<font color=#0000ff>if</font>(cp) cout &lt;&lt; <font color=#004488>"cast successful"</font>;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The template argument for
<B>dynamic_cast</B> is the type you want the function to produce, and this is
the return value for the function. The function argument is what you are trying
to cast from.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Normally you might be hunting for one
type (triangles to turn purple, for instance), but the following example
fragment can be used if you want to count the number of various
shapes.</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>  Circle* cp = <font color=#0000ff>dynamic_cast</font>&lt;Circle*&gt;(sh);
  Square* sp = <font color=#0000ff>dynamic_cast</font>&lt;Square*&gt;(sh);
  Triangle* tp = <font color=#0000ff>dynamic_cast</font>&lt;Triangle*&gt;(sh);</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><A NAME="_Toc305593310"></A><A NAME="_Toc305628782"></A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course this is contrived &#8211;
you&#8217;d probably put a <B>static</B> data member in each type and increment
it in the constructor. You would do something like that <I>if</I> you had
control of the source code for the class and could change it. Here&#8217;s an
example that counts shapes using both the <B>static</B> member approach and
<B>dynamic_cast</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Rtshapes.cpp</font>

⌨️ 快捷键说明

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