We can do even better by using a map that associates pointers to type_info objects with a vector of Trash pointers. Since a map requires an ordering predicate, we provide one named TInfoLess that calls type_info::before( ). As we insert Trash pointers into the map, they are associated automatically with their type_info key.
//: C08:Recycle2.cpp
// A Trash Recycler
#include
#include
#include
#include
#include
#include
#include
#include "../purge.h"
using namespace std;
class Trash {
float wt;
public:
Trash(float wt) : wt(wt) {}
virtual float value() const = 0;
float weight() const { return wt; }
virtual ~Trash() { cout << "~Trash()\n"; }
};
class Aluminum : public Trash {
static float val;
public:
Aluminum(float wt) : Trash(wt) {}
float value() const { return val; }
static void value(float newval) {
val = newval;
}
};
float Aluminum::val = 1.67;
class Paper : public Trash {
static float val;
public:
Paper(float wt) : Trash(wt) {}
float value() const { return val; }
static void value(float newval) {
val = newval;
}
};
float Paper::val = 0.10;
class Glass : public Trash {
static float val;
public:
Glass(float wt) : Trash(wt) {}
float value() const { return val; }
static void value(float newval) {
val = newval;
}
};
float Glass::val = 0.23;
// Comparator for type_info pointers
struct TInfoLess {
bool operator()(const type_info* t1, const type_info* t2)
const {
return t1->before(*t2);
}
};
typedef map
TrashMap;
// Sums up the value of the Trash in a bin:
void sumValue(const TrashMap::value_type& p, ostream& os) {
vector
float val = 0;
while(tally != p.second.end()) {
val += (*tally)->weight() * (*tally)->value();
os << "weight of "
<< p.first->name() // type_info::name()
<< " = " << (*tally)->weight() << endl;
tally++;
}
os << "Total value = " << val << endl;
}
int main() {
srand(time(0)); // Seed random number generator
TrashMap bin;
// Fill up the Trash bin:
for(int i = 0; i < 30; i++) {
Trash* tp;
switch(rand() % 3) {
case 0 :
tp = new Aluminum((rand() % 1000)/10.0);
break;
case 1 :
tp = new Paper((rand() % 1000)/10.0);
break;
case 2 :
tp = new Glass((rand() % 1000)/10.0);
break;
}
bin[&typeid(*tp)].push_back(tp);
}
// Print sorted results
for(TrashMap::iterator p = bin.begin();
p != bin.end(); ++p) {
sumValue(*p, cout);
purge(p->second);
}
} ///:~
We’ve modified sumValue( ) to call type_info::name( ) directly, since the type_info object is now available there as the first member of the TrashMap::value_type pair. This avoids the extra call to typeid to get the name of the type of Trash being processed that was necessary in the previous version of this program.
Mechanism and overhead of RTTI
Typically, RTTI is implemented by placing an additional pointer in a class’s virtual function table. This pointer points to the type_info structure for that particular type. The effect of a typeid( ) expression is quite simple: the virtual function table pointer fetches the type_info pointer, and a reference to the resulting type_info structure is produced. Since this is just a two-pointer dereference operation, it is a constant time operation.
For a dynamic_cast
Because the library routine used for dynamic_cast must check through a list of base classes, the overhead for dynamic_cast may be higher than typeid( ) (but of course you get different information, which may be essential to your solution), and it may take more time to discover a base class than a derived class. In addition, dynamic_cast 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 dynamic_cast.
Summary