📄 ch07.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN"><HTML><HEAD> <META NAME="Author" Content="Steph Mineart"> <META HTTP-EQUIV="Content-Type" CONTENT="text/html;CHARSET=iso-8859-1"> <TITLE>ANSI/ISO C++ Professional Programmer's Handbook - Chapter 7 - Runtime Type Identification</TITLE> <link rel="stylesheet" TYPE="text/css" href="/includes/stylesheets/ebooks.css"></head><BODY TEXT="#000000" BGCOLOR="#FFFFFF"><CENTER><H1><img src="/publishers/que/series/professional/0789720221/button/que.gif" WIDTH="171" HEIGHT="66" ALIGN="BOTTOM" BORDER="0"><BR>ANSI/ISO C++ Professional Programmer's Handbook</H1></CENTER><CENTER> <P><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> <HR></CENTER><H1 align="center">7</H1><h1 align="center"> Runtime Type Identification</h1><address>by Danny Kalev </address><ul> <li><a href="#Heading1">Introduction</a> <li><a href="#Heading2">Structure Of This Chapter</a> <li><a href="#Heading3"> Making Do Without RTTI</a> <ul> <li><a href="#Heading4">Virtual member functions can provide a reasonable level of dynamic typing without the need for additional RTTI support. A well-designed class hierarchy can define a meaningful operation for every virtual member function that is declared in the base class.</a> </ul> <li><a href="#Heading5">RTTI constituents</a> <ul> <li><a href="#Heading6">RTTI Is Applicable to Polymorphic Objects Exclusively</a> <li><a href="#Heading7">Class type_info</a> <li><a href="#Heading8">Operator typeid</a> <li><a href="#Heading9">Operator dynamic_cast<></a> <li><a href="#Heading10">Other Uses of dynamic_cast<></a> </ul> <li><a href="#Heading11">The Cost of Runtime Type Information</a> <ul> <li><a href="#Heading12">Memory Overhead</a> <li><a href="#Heading13">Runtime Type Information of Polymorphic Objects</a> <li><a href="#Heading14">Additional Overhead</a> <li><a href="#Heading15">RTTI Support Can Usually Be Toggled</a> <li><a href="#Heading16">typeid Versus dynamic_cast<></a> </ul> <li><a href="#Heading17">Conclusions</a> </ul><hr size=4><h2><a name="Heading1">Introduction</a></h2><p>Originally, C++ did not provide standardized support for runtime type information (RTTI). Furthermore, its creators balked at the idea of adding RTTI support for at least two reasons. First, they wanted to preserve backward compatibility with C. Secondly, they were concerned about efficiency. Other RTTI-enabled languages, such as Smalltalk and Lisp, were characterized by their notoriously sluggish performance. The performance penalty of dynamic type checking results from the relatively slow process of retrieving the object's type at runtime as well as from the additional information that the system needs to store for every type. C++ designers wanted to preserve the efficiency of C. </p><p>Another claim against the addition of RTTI to the language was that, in many cases, the use of virtual member functions could serve as an alternative to explicit runtime type checking. However, the addition of multiple inheritance (and consequently, of virtual inheritance) to C++ gave overwhelming ammunition to the proponents of RTTI (multiple inheritance is discussed in Chapter 5, "Object-Oriented Programming and Design"); it became apparent that under some circumstances, static type checking and virtual functions were insufficient.</p><p>Eventually, the C++ standardization committee approved the addition of RTTI to the language. Two new operators, <tt>dynamic_cast<></tt> and <tt>typeid</tt>, were introduced. In addition, the class <tt>std::type_info</tt> was added to the Standard Library.</p><h2> <a name="Heading2">Structure Of This Chapter</a></h2><p>This chapter consists of three major parts. The limitations of virtual functions are presented first. Then, the standard RTTI constituents are explained and exemplified. Finally, RTTI performance and design issues are discussed. </p><p></p><h2> <a name="Heading3"> Making Do Without RTTI</a></h2><p> <a name="Heading4">Virtual member functions can provide a reasonable level of dynamic typing without the need for additional RTTI support. A well-designed class hierarchy can define a meaningful operation for every virtual member function that is declared in the base class.</a></p><p>Suppose you have to develop a file manager application as a component of a GUI-based operating system. The files in this system are represented as icons that respond to the right click of a mouse, displaying a menu with options such as open, close, read, and so on. The underlying implementation of the file system relies on a class hierarchy that represents files of various types. In a well-designed class hierarchy, there is usually an abstract class serving as an interface:</p><pre><tt>class File //abstract, all members are pure virtual</tt><tt>{</tt><tt> public: virtual void open() =0; </tt><tt> public: virtual void read() =0;</tt><tt> public: virtual void write() =0;</tt><tt> public: virtual ~File () =0;</tt><tt>};</tt><tt>File::~File () //pure virtual destructor must be defined</tt><tt>{}</tt></pre><p>At a lower level in the hierarchy, you have a set of derived classes that implement the common interface that they inherit from <tt>File</tt>. Each of these subclasses represents a different family of files. To simplify the discussion, assume that there are only two file types in this system: binary .exe files and text files.</p><pre><tt>class BinaryFile : public File</tt><tt>{</tt><tt>public:</tt><tt> void open () { OS_execute(this); } //implement the pure virtual function</tt><tt> //...other member functions</tt><tt>};</tt><tt>class TextFile : public File</tt><tt>{</tt><tt>public:</tt><tt> void open () { Activate_word_processor (this); } </tt><tt> //...other member functions of File are implemented here</tt><tt> void virtual print(); // an additional member function</tt><tt>};</tt></pre><p>The pure virtual function <tt>open()</tt> is implemented in every derived class, according to the type of the file. Thus, in a <tt>TextFile</tt> object, <tt>open()</tt> activates a word processor, whereas a <tt>BinaryFile</tt> object invokes the operating system's API function <tt>OS_execute()</tt>, which in turn executes the program that is stored in the binary file.</p><p>There are several differences between a binary file and a text file. For example, a text file can be printed directly on a screen or a printer because it consists of a sequence of printable characters. Conversely, a binary file with an .exe extension contains a stream of bits; it cannot be printed or displayed directly on a screen. It must be converted to a text file first, usually by a utility that translates the binary data into their symbolic representations. (For instance, the sequence <tt>0110010</tt> in an executable file can be replaced by a corresponding <cite>move esp, ebp</cite><i> </i>assembly directive.) In other words, an executable file must be converted to a text file in order to be viewed or printed. Therefore, the member function <tt>print()</tt> appears only in class <tt>TextFile</tt>.</p><p>In this file manager, right-clicking the mouse on a file icon opens a menu of messages (options) to which the object can respond. For that purpose, the operating system has a function that takes a reference to a <tt>File</tt>:</p><pre><tt>OnRightClick (File & file); //operating system's API function</tt></pre><p>Obviously, no object of class <tt>File</tt> can be instantiated because <tt>File</tt> is an abstract class (see Chapter 5). However, the function <tt>OnRightClick()</tt> can accept any object that is derived from <tt>File</tt>. When the user right-clicks on a file icon and chooses the option Open, for instance, <tt>OnRightClick</tt> invokes the virtual member function <tt>open</tt> of its argument, and the appropriate member function is called. For example</p><pre><tt>OnRightClick (File & file)</tt><tt>{</tt><tt> switch (message)</tt><tt> {</tt><tt> //...</tt><tt> case m_open:</tt><tt> file.open();</tt><tt> break;</tt><tt> }</tt><tt>}</tt></pre><p>So far, so good. You have implemented a polymorphic class hierarchy and a function that does not depend on the dynamic type of its argument. In this case, the language support for virtual functions was sufficient for your purposes; you did not need any explicit runtime type information (RTTI). Well, not exactly. You might have noticed the lack of file printing support. Look at the definition of class <tt>TextFile</tt> again:</p><pre><tt>class TextFile : public File</tt><tt>{</tt><tt>public:</tt><tt> void open () { Activate_word_processor (this); } </tt><tt> void virtual print();</tt><tt>};</tt></pre><p>The member function <tt>print()</tt> is not a part of the common interface that is implemented by all files in your system. It would be a design error to move <tt>print()</tt> to the abstract class <tt>File</tt> because binary files are nonprintable and cannot define a meaningful operation for it. Then again, <tt>OnRightClick()</tt> has to support file printing when it handles a text file. In this case, ordinary polymorphism in the form of virtual member functions will not do. <tt>OnRightClick()</tt> only knows that its argument is derived from <tt>File</tt>. However, this information is not sufficient to tell whether the actual object is printable. Clearly, <tt>OnRightClick()</tt> needs more information about the dynamic type of its argument in order to properly handle file printing. This is where the need for runtime type information arises. Before delving into the implementation of <tt>OnRightClick()</tt>, an overview of RTTI constituents and their role is necessary.</p><h2> <a name="Heading5">RTTI constituents</a></h2><p>The operators <tt>typeid</tt> and <tt>dynamic_cast<></tt> offer two complementary forms of accessing the runtime type information of their operand. The operand's runtime type information itself is stored in a <tt>type_info</tt> object. This section exemplifies how these three constituents are used.</p><h3> <a name="Heading6">RTTI Is Applicable to Polymorphic Objects Exclusively</a></h3><p>It is important to realize that RTTI is applicable solely to polymorphic objects. A class must have at least one virtual member function in order to have RTTI support for its objects. C++ does not offer RTTI support for non-polymorphic classes and primitive types. This restriction is just common sense -- a fundamental type such as <tt>double</tt> or a concrete class such as <tt>string</tt> cannot change its type at runtime. Therefore, there is no need to detect their dynamic types because they are identical to their static types. But there is also a practical reason for confining RTTI support to polymorphic classes exclusively, as you will see momentarily.</p><p>As you probably know, every object that has at least one virtual member function also contains a special data member that is added by the compiler (more on this in Chapter 13, "C Language Compatibility Issues"). This member is a pointer to the virtual function table. The runtime type information is stored in this table, as is a pointer to a <tt>std::type_info</tt> object.</p><h3> <a name="Heading7">Class type_info</a></h3><p>For every distinct type, C++ instantiates a corresponding RTTI object that contains the necessary runtime type information. The RTTI object is an instance of the standard class <tt>std::type_info</tt> or an implementation-defined class derived from it. (<tt>std::type_info</tt> is defined in the standard header <typeinfo>). Th<tt>is</tt> object is owned by the implementation and cannot be altered in any way by the programmer. The interface of <tt>type_info</tt> looks similar to the following (namespaces will be covered in Chapter 8, "Namespaces"):</p><pre><tt>namespace std { //class type_info is declared in namespace std</tt><tt> class type_info</tt><tt> {</tt><tt> public:</tt><tt> virtual ~type_info(); //type_info can serve as a base class</tt><tt> bool operator==(const type_info& rhs ) const; // enable comparison </tt><tt> bool operator!=(const type_info& rhs ) const; // return !( *this == rhs)</tt><tt> bool before(const type_info& rhs ) const; // ordering</tt><tt> const char* name() const; //return a C-string containing the type's name</tt><tt> private:</tt><tt> //objects of this type cannot be copied</tt><tt> type_info(const type_info& rhs );</tt><tt> type_info& operator=(const type_info& rhs);</tt><tt> }; //type_info</tt>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -