📄 chap09.htm
字号:
sumValue(glassBin, out);
sumValue(bin, out);
purge(bin);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><A NAME="_Toc305593314"></A><A NAME="_Toc305628786"></A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The nature of this problem is that the
trash is thrown unclassified into a single bin, so the specific type information
is lost. But later, the specific type information must be recovered to properly
sort the trash, and so RTTI is used. In Chapter XX, an RTTI system was inserted
into the class hierarchy, but as you can see here, it’s more convenient to
use C++’s built-in
RTTI.</FONT><A NAME="_Toc312374148"></A><A NAME="_Toc519042096"></A><BR></P></DIV>
<A NAME="Heading303"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Mechanism & overhead of RTTI<BR><A NAME="Index575"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Typically, RTTI is implemented by placing
an additional pointer in the
VTABLE<A NAME="Index576"></A><A NAME="Index577"></A>. This pointer points to the
<B>typeinfo</B> <A NAME="Index578"></A><A NAME="Index579"></A>structure for that
particular type. (Only one instance of the <B>typeinfo</B> structure is created
for each new class.) So the effect of a <B>typeid( )</B> expression is
quite simple: The VPTR is used to fetch the <B>typeinfo</B> pointer, and a
reference to the resulting <B>typeinfo</B> structure is produced. Also, this is
a deterministic process – you always know how long it’s going to
take.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For a
<B>dynamic_cast<destination*>(source_pointer)</B>, most cases are quite
straightforward: <B>source_pointer</B>’s RTTI information is retrieved,
and RTTI information for the type <B>destination*</B> is fetched. Then a library
routine determines whether <B>source_pointer</B>’s type is of type
<B>destination*</B> or a base class of <B>destination*</B>. The pointer it
returns may be slightly adjusted because of multiple
inheritance<A NAME="Index580"></A><A NAME="Index581"></A><A NAME="Index582"></A>
if the base type isn’t the first base of the derived class. The situation
is (of course) more complicated with multiple inheritance where a base type may
appear more than once in an inheritance hierarchy and where virtual base classes
are used.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because the library routine used for
<B>dynamic_cast</B> must check through a list of base classes, the overhead for
<B>dynamic_cast</B> is higher than <B>typeid( )</B> (but of course you get
different information, which may be essential to your solution), and it’s
nondeterministic because it may take more time to discover a base class than a
derived class. In addition, <B>dynamic_cast</B> allows you to compare any type
to any other type; you aren’t restricted to comparing types within the
same hierarchy. This adds extra overhead to the library routine used by
<B>dynamic_cast</B>.</FONT><A NAME="_Toc305593315"></A><A NAME="_Toc305628787"></A><A NAME="_Toc312374149"></A><A NAME="_Toc519042097"></A><BR></P></DIV>
<A NAME="Heading304"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Creating your own RTTI<BR><A NAME="Index583"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If your compiler doesn’t yet
support RTTI, you can build it into your class libraries quite easily. This
makes sense because RTTI was added to the language after observing that
virtually all class libraries had some form of it anyway (and it was relatively
“free” after exception handling was added because exceptions require
exact knowledge of type information).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Essentially, RTTI requires only a virtual
function to identify the exact type of the class, and a function to take a
pointer to the base type and cast it down to the more derived type; this
function must produce a pointer to the more derived type. (You may also wish to
handle references.) There are a number of approaches to implement your own RTTI,
but all require a unique identifier for each class and a virtual function to
produce type information. The following uses a <B>static</B> member function
called <B>dynacast( )</B> that calls a type information function
<B>dynamic_type( )</B>. Both functions must be defined for each new
derivation:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Selfrtti.cpp</font>
<font color=#009900>// Your own RTTI system</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include <font color=#004488>"..</font><font color=#004488>/purge.h"</font>
#include <iostream>
#include <vector>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> Security {
<font color=#0000ff>protected</font>:
<font color=#0000ff>enum</font> { baseID = 1000 };
<font color=#0000ff>public</font>:
<font color=#0000ff>virtual</font> <font color=#0000ff>int</font> dynamic_type(<font color=#0000ff>int</font> id) {
<font color=#0000ff>if</font>(id == baseID) <font color=#0000ff>return</font> 1;
<font color=#0000ff>return</font> 0;
}
};
<font color=#0000ff>class</font> Stock : <font color=#0000ff>public</font> Security {
<font color=#0000ff>protected</font>:
<font color=#0000ff>enum</font> { typeID = baseID + 1 };
<font color=#0000ff>public</font>:
<font color=#0000ff>int</font> dynamic_type(<font color=#0000ff>int</font> id) {
<font color=#0000ff>if</font>(id == typeID) <font color=#0000ff>return</font> 1;
<font color=#0000ff>return</font> Security::dynamic_type(id);
}
<font color=#0000ff>static</font> Stock* dynacast(Security* s) {
<font color=#0000ff>if</font>(s->dynamic_type(typeID))
<font color=#0000ff>return</font> (Stock*)s;
<font color=#0000ff>return</font> 0;
}
};
<font color=#0000ff>class</font> Bond : <font color=#0000ff>public</font> Security {
<font color=#0000ff>protected</font>:
<font color=#0000ff>enum</font> { typeID = baseID + 2 };
<font color=#0000ff>public</font>:
<font color=#0000ff>int</font> dynamic_type(<font color=#0000ff>int</font> id) {
<font color=#0000ff>if</font>(id == typeID) <font color=#0000ff>return</font> 1;
<font color=#0000ff>return</font> Security::dynamic_type(id);
}
<font color=#0000ff>static</font> Bond* dynacast(Security* s) {
<font color=#0000ff>if</font>(s->dynamic_type(typeID))
<font color=#0000ff>return</font> (Bond*)s;
<font color=#0000ff>return</font> 0;
}
};
<font color=#0000ff>class</font> Commodity : <font color=#0000ff>public</font> Security {
<font color=#0000ff>protected</font>:
<font color=#0000ff>enum</font> { typeID = baseID + 3 };
<font color=#0000ff>public</font>:
<font color=#0000ff>int</font> dynamic_type(<font color=#0000ff>int</font> id) {
<font color=#0000ff>if</font>(id == typeID) <font color=#0000ff>return</font> 1;
<font color=#0000ff>return</font> Security::dynamic_type(id);
}
<font color=#0000ff>static</font> Commodity* dynacast(Security* s) {
<font color=#0000ff>if</font>(s->dynamic_type(typeID))
<font color=#0000ff>return</font> (Commodity*)s;
<font color=#0000ff>return</font> 0;
}
<font color=#0000ff>void</font> special() {
cout << <font color=#004488>"special Commodity function\n"</font>;
}
};
<font color=#0000ff>class</font> Metal : <font color=#0000ff>public</font> Commodity {
<font color=#0000ff>protected</font>:
<font color=#0000ff>enum</font> { typeID = baseID + 4 };
<font color=#0000ff>public</font>:
<font color=#0000ff>int</font> dynamic_type(<font color=#0000ff>int</font> id) {
<font color=#0000ff>if</font>(id == typeID) <font color=#0000ff>return</font> 1;
<font color=#0000ff>return</font> Commodity::dynamic_type(id);
}
<font color=#0000ff>static</font> Metal* dynacast(Security* s) {
<font color=#0000ff>if</font>(s->dynamic_type(typeID))
<font color=#0000ff>return</font> (Metal*)s;
<font color=#0000ff>return</font> 0;
}
};
<font color=#0000ff>int</font> main() {
vector<Security*> portfolio;
portfolio.push_back(<font color=#0000ff>new</font> Metal);
portfolio.push_back(<font color=#0000ff>new</font> Commodity);
portfolio.push_back(<font color=#0000ff>new</font> Bond);
portfolio.push_back(<font color=#0000ff>new</font> Stock);
vector<Security*>::iterator it =
portfolio.begin();
<font color=#0000ff>while</font>(it != portfolio.end()) {
Commodity* cm = Commodity::dynacast(*it);
<font color=#0000ff>if</font>(cm) cm->special();
<font color=#0000ff>else</font> cout << <font color=#004488>"not a Commodity"</font> << endl;
it++;
}
cout << <font color=#004488>"cast from intermediate pointer:\n"</font>;
Security* sp = <font color=#0000ff>new</font> Metal;
Commodity* cp = Commodity::dynacast(sp);
<font color=#0000ff>if</font>(cp) cout << <font color=#004488>"it's a Commodity\n"</font>;
Metal* mp = Metal::dynacast(sp);
<font color=#0000ff>if</font>(mp) cout << <font color=#004488>"it's a Metal too!\n"</font>;
purge(portfolio);
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Each subclass must create its own
<B>typeID</B>, redefine the <B>virtual dynamic_type( )</B> function to
return that <B>typeID</B>, and define a <B>static</B> member called
<B>dynacast( )</B>, which takes the base pointer (or a pointer at any level
in a deeper hierarchy – in that case, the pointer is simply
upcast).</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the classes derived from
<B>Security</B>, you can see that each defines its own <B>typeID</B> enumeration
by adding to <B>baseID</B>. It’s essential that <B>baseID</B> be directly
accessible in the derived class because the <B>enum</B> must be evaluated at
compile-time, so the usual approach of reading private data with an
<B>inline</B> function would fail. This is a good example of the need for the
<B>protected</B> <A NAME="Index584"></A>mechanism.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>enum</B> <B>baseID</B> establishes
a base identifier for all types derived from <B>Security</B>. That way, if an
identifier clash ever occurs, you can change all the identifiers by changing the
base value. (However, because this scheme doesn’t compare different
inheritance trees, an identifier clash is unlikely). In all the classes, the
class identifier number is <B>protected</B>, so it’s directly available to
derived classes but not to the end user.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This example illustrates what built-in
RTTI must cope with. Not only must you be able to determine the exact type, you
must also be able to find out whether your exact type is <I>derived from</I> the
type you’re looking for. For example, <B>Metal </B>is derived from
<B>Commodity</B>,<B> </B>which has a function called <B>special( )</B>, so
if you have a <B>Metal</B> object you can call <B>special( )</B> for it. If
<B>dynamic_type( ) </B>told you only the exact type of the object, you
could ask it if a <B>Metal</B> were a <B>Commodity</B>, and it would say
“no,” which is untrue. Therefore, the system must be set up so it
will properly cast to intermediate types in a hierarchy as well as exact
types.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>dynacast( ) </B>function
determines the type information by calling the <B>virtual
dynamic_type( )</B> function for the <B>Security</B> pointer it’s
passed. This function takes an argument of the <B>typeID </B>for the class
you’re trying to cast to. It’s a virtual function, so the function
body is the one for the exact type of the object. Each
<B>dynamic_type( )</B> function first checks to see if the identifier it
was passed is an exact match for its own type. If that isn’t true, it must
check to see if it matches a base type; this is accomplished by making a call to
the base class <B>dynamic_type( )</B>. Just like a recursive function call,
each <B>dynamic_type( ) </B>checks against its own identifier. If it
doesn’t find a match, it returns the result of calling the base class
<B>dynamic_type( )</B>. When the root of the hierarchy is reached, zero<B>
</B>is returned to indicate no match was found.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If <B>dynamic_type( )</B> returns
one (for “true”) the object pointed to is either the exact type
you’re asking about or derived from that type, and <B>dynacast( )</B>
takes the <B>Security</B> pointer and casts it to the desired type. If the
return value is false, <B>dynacast( )</B> returns zero to indicate the cast
was unsuccessful. In this way it work
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -