📄 chap09.htm
字号:
<< endl;
}
X* xp = 0;
<font color=#0000ff>try</font> {
<font color=#0000ff>typeid</font>(*xp); <font color=#009900>// Throws exception</font>
} <font color=#0000ff>catch</font>(<font color=#0000ff>bad_typeid</font>) {
cout << <font color=#004488>"Bad typeid() expression"</font> << endl;
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The failure, of course, is because
<B>b</B> doesn’t actually point to an <B>X</B> object. If an exception was
not thrown here, then <B>xr</B> would be unbound, and the guarantee that all
objects or references are constructed storage would be broken.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An exception is also thrown if you try to
dereference a null pointer in the process of calling
<B>typeid( )<A NAME="Index562"></A><A NAME="Index563"></A></B>. The
Standard C++ exception is called
<B>bad_typeid<A NAME="Index564"></A><A NAME="Index565"></A><A NAME="Index566"></A></B>.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here (unlike the reference example above)
you can avoid the exception by checking for a nonzero pointer value before
attempting the operation; this is the preferred
practice.</FONT><A NAME="_Toc305593312"></A><A NAME="_Toc305628784"></A><A NAME="_Toc312374145"></A><A NAME="_Toc519042093"></A><BR></P></DIV>
<A NAME="Heading300"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Multiple
inheritance<BR><A NAME="Index567"></A><A NAME="Index568"></A><A NAME="Index569"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Of course, the RTTI mechanisms must work
properly with all the complexities of multiple inheritance, including
<B>virtual</B> base classes:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:RTTIandMultipleInheritance.cpp</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include <iostream>
#include <typeinfo>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
<font color=#0000ff>class</font> BB {
<font color=#0000ff>public</font>:
<font color=#0000ff>virtual</font> <font color=#0000ff>void</font> f() {}
<font color=#0000ff>virtual</font> ~BB() {}
};
<font color=#0000ff>class</font> B1 : <font color=#0000ff>virtual</font> <font color=#0000ff>public</font> BB {};
<font color=#0000ff>class</font> B2 : <font color=#0000ff>virtual</font> <font color=#0000ff>public</font> BB {};
<font color=#0000ff>class</font> MI : <font color=#0000ff>public</font> B1, <font color=#0000ff>public</font> B2 {};
<font color=#0000ff>int</font> main() {
BB* bbp = <font color=#0000ff>new</font> MI; <font color=#009900>// Upcast</font>
<font color=#009900>// Proper name detection:</font>
cout << <font color=#0000ff>typeid</font>(*bbp).name() << endl;
<font color=#009900>// Dynamic_cast works properly:</font>
MI* mip = <font color=#0000ff>dynamic_cast</font><MI*>(bbp);
<font color=#009900>// Can't force old-style cast:</font>
<font color=#009900>//! MI* mip2 = (MI*)bbp; // Compile error</font>
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>typeid( ) </B>properly detects
the name of the actual object, even through the <B>virtual</B> base class
pointer. The <B>dynamic_cast</B> also works correctly. But the compiler
won’t even allow you to try to force a cast the old way:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>MI* mip = (MI*)bbp; <font color=#009900>// Compile-time error</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It knows this is never the right thing to
do, so it requires that you use a
<B>dynamic_cast</B>.</FONT><A NAME="_Toc305593313"></A><A NAME="_Toc305628785"></A><A NAME="_Toc312374146"></A><A NAME="_Toc519042094"></A><BR></P></DIV>
<A NAME="Heading301"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Sensible uses for RTTI<BR><A NAME="Index570"></A></H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Because it allows you to discover type
information from an anonymous polymorphic pointer, RTTI is ripe for misuse
<A NAME="Index571"></A>by the novice because RTTI may make sense before virtual
functions do. For many people coming from a procedural background, it’s
very difficult not to organize their programs into sets of <B>switch</B>
statements. They could accomplish this with RTTI and thus lose the very
important value of polymorphism <A NAME="Index572"></A>in code development and
maintenance. The intent of C++ is that you use virtual functions throughout your
code, and you only use RTTI when you must.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">However, using virtual functions as they
are intended requires that you have control of the base-class definition because
at some point in the extension of your program you may discover the base class
doesn’t include the virtual function you need. If the base class comes
from a library or is otherwise controlled by someone else, a solution to the
problem is RTTI: You can inherit a new type and add your extra member function.
Elsewhere in the code you can detect your particular type and call that member
function. This doesn’t destroy the polymorphism and extensibility of the
program, because adding a new type will not require you to hunt for switch
statements. However, when you add new code in your main body that requires your
new feature, you’ll have to detect your particular type.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Putting a feature in a base class might
mean that, for the benefit of one particular class, all the other classes
derived from that base require some meaningless stub of a virtual function. This
makes the interface less clear and annoys those who must redefine pure virtual
functions when they derive from that base class. For example, suppose that in
the <B>Wind5.cpp</B> program in Chapter XX you wanted to clear the spit valves
of all the instruments in your orchestra that had them. One option is to put a
<B>virtual ClearSpitValve( ) </B>function in the base class
<B>Instrument</B>, but this is confusing because it implies that
<B>Percussion</B> and <B>electronic</B> instruments also have spit valves. RTTI
provides a much more reasonable solution in this case because you can place the
function in the specific class (<B>Wind </B>in this case) where it’s
appropriate.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Finally, RTTI will sometimes solve
efficiency <A NAME="Index573"></A><A NAME="Index574"></A>problems. If your code
uses polymorphism in a nice way, but it turns out that one of your objects
reacts to this general-purpose code in a horribly inefficient way, you can pick
that type out using RTTI and write case-specific code to improve the
efficiency.</FONT><A NAME="_Toc312374147"></A><A NAME="_Toc519042095"></A><BR></P></DIV>
<A NAME="Heading302"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Revisiting the trash recycler</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here’s the trash recycling
simulation from Chapter XX, rewritten to use RTTI instead of building the
information into the class hierarchy:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: C09:Recycle2.cpp</font>
<font color=#009900>// Chapter XX example w/ RTTI</font>
<font color=#009900>//{L} ../TestSuite/Test</font>
#include <font color=#004488>"..</font><font color=#004488>/purge.h"</font>
#include <fstream>
#include <vector>
#include <typeinfo>
#include <cstdlib>
#include <ctime>
<font color=#0000ff>using</font> <font color=#0000ff>namespace</font> std;
ofstream out(<font color=#004488>"recycle2.out"</font>);
<font color=#0000ff>class</font> Trash {
<font color=#0000ff>float</font> _weight;
<font color=#0000ff>public</font>:
Trash(<font color=#0000ff>float</font> wt) : _weight(wt) {}
<font color=#0000ff>virtual</font> <font color=#0000ff>float</font> value() <font color=#0000ff>const</font> = 0;
<font color=#0000ff>float</font> weight() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> _weight; }
<font color=#0000ff>virtual</font> ~Trash() { out << <font color=#004488>"~Trash()\n"</font>; }
};
<font color=#0000ff>class</font> Aluminum : <font color=#0000ff>public</font> Trash {
<font color=#0000ff>static</font> <font color=#0000ff>float</font> val;
<font color=#0000ff>public</font>:
Aluminum(<font color=#0000ff>float</font> wt) : Trash(wt) {}
<font color=#0000ff>float</font> value() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> val; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> value(<font color=#0000ff>int</font> newval) {
val = newval;
}
};
<font color=#0000ff>float</font> Aluminum::val = 1.67;
<font color=#0000ff>class</font> Paper : <font color=#0000ff>public</font> Trash {
<font color=#0000ff>static</font> <font color=#0000ff>float</font> val;
<font color=#0000ff>public</font>:
Paper(<font color=#0000ff>float</font> wt) : Trash(wt) {}
<font color=#0000ff>float</font> value() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> val; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> value(<font color=#0000ff>int</font> newval) {
val = newval;
}
};
<font color=#0000ff>float</font> Paper::val = 0.10;
<font color=#0000ff>class</font> Glass : <font color=#0000ff>public</font> Trash {
<font color=#0000ff>static</font> <font color=#0000ff>float</font> val;
<font color=#0000ff>public</font>:
Glass(<font color=#0000ff>float</font> wt) : Trash(wt) {}
<font color=#0000ff>float</font> value() <font color=#0000ff>const</font> { <font color=#0000ff>return</font> val; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> value(<font color=#0000ff>int</font> newval) {
val = newval;
}
};
<font color=#0000ff>float</font> Glass::val = 0.23;
<font color=#009900>// Sums up the value of the Trash in a bin:</font>
<font color=#0000ff>template</font><<font color=#0000ff>class</font> Container> <font color=#0000ff>void</font>
sumValue(Container& bin, ostream& os) {
<font color=#0000ff>typename</font> Container::iterator tally =
bin.begin();
<font color=#0000ff>float</font> val = 0;
<font color=#0000ff>while</font>(tally != bin.end()) {
val += (*tally)->weight() * (*tally)->value();
os << <font color=#004488>"weight of "</font>
<< <font color=#0000ff>typeid</font>(*tally).name()
<< <font color=#004488>" = "</font> << (*tally)->weight() << endl;
tally++;
}
os << <font color=#004488>"Total value = "</font> << val << endl;
}
<font color=#0000ff>int</font> main() {
srand(time(0)); <font color=#009900>// Seed random number generator</font>
vector<Trash*> bin;
<font color=#009900>// Fill up the Trash bin:</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 30; i++)
<font color=#0000ff>switch</font>(rand() % 3) {
<font color=#0000ff>case</font> 0 :
bin.push_back(<font color=#0000ff>new</font> Aluminum(rand() % 100));
<font color=#0000ff>break</font>;
<font color=#0000ff>case</font> 1 :
bin.push_back(<font color=#0000ff>new</font> Paper(rand() % 100));
<font color=#0000ff>break</font>;
<font color=#0000ff>case</font> 2 :
bin.push_back(<font color=#0000ff>new</font> Glass(rand() % 100));
<font color=#0000ff>break</font>;
}
<font color=#009900>// Note difference w/ chapter 14: Bins hold</font>
<font color=#009900>// exact type of object, not base type:</font>
vector<Glass*> glassBin;
vector<Paper*> paperBin;
vector<Aluminum*> alBin;
vector<Trash*>::iterator sorter = bin.begin();
<font color=#009900>// Sort the Trash:</font>
<font color=#0000ff>while</font>(sorter != bin.end()) {
Aluminum* ap =
<font color=#0000ff>dynamic_cast</font><Aluminum*>(*sorter);
Paper* pp =
<font color=#0000ff>dynamic_cast</font><Paper*>(*sorter);
Glass* gp =
<font color=#0000ff>dynamic_cast</font><Glass*>(*sorter);
<font color=#0000ff>if</font>(ap) alBin.push_back(ap);
<font color=#0000ff>if</font>(pp) paperBin.push_back(pp);
<font color=#0000ff>if</font>(gp) glassBin.push_back(gp);
sorter++;
}
sumValue(alBin, out);
sumValue(paperBin, out);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -