TITLE: evil downcasting (Newsgroups: comp.lang.c++.moderated, 10 Dec 97) HENSEN: Bill Hensen > You Can't Get There from Here. > > I am trying to access attributes (or methods) of a subclass, when all > I have is its pointer (or reference), of type base class. I cannot get > to any of these subclass items if they are not defined virtually in the > base class. OTTINGER: Tim Ottinger Well, there are a few ways to recover type information when it has been lost through upcasting. There is dynamic_cast (which is not very nice), double-dispatch (which is only tedious), comparing type_info (which is worse), and the use of type flags to provide some safety in downcasts (which is odious), and the use of typeflags to select non-member functions (foul, horrible, disgusting), and the use of unchecked downcasts (unspeakably foul, the stench of the charnel pit, a steamy breeze from the toxic waste dump, the breath of a mule, you get the idea), or the loading of base classes with virtual functions that are only supported by a very few derived classes (The Unpardonable). We'd like to never have this problem. But generally the ugliness of a dynamic_cast is far less awful than many alternatives. Avoiding the problem is better still. If you are in this situation, the question isn't "what is the clean way to do this" (though I hope someone invents one), but "how can I limit the damage". You want whatever solution you create to add no new dependencies. For some examples of damage control, look at the dual inheritance hierarchy paper on our web site. Sorry I can't give you anything much better than that. [ For completeness, I included this response concerning the visitor pattern. This is a very useful technique, but keep in mind that its brittleness is that the class is not "closed"; when you add new derived classes, you have to add a new method to the visitor class. -adc ] LORDE: Dave Lorde , 11 Nov 97 You *can* get there from here without all that nasty down-casting if you're prepared to take the scenic route. There is a pattern that will help in some circumstances, called the Visitor pattern. In the Visitor pattern, you give each class a method called 'accept' which takes a Visitor& argument: void Widget::accept(Visitor& visitor) { visitor.visit(this); } The accept method just passes it's 'this' pointer to the visitor's visit method. What does this achieve? Well, the Visitor class happens to be the base class of a particular type of visitor that will do some particular operation on each of your StockItems (print, display, edit, etc.). For example, the EditStockItemVisitor class derives from Visitor, and has an overloaded visit method for every StockItem derived class: class Visitor { public: virtual ~Visitor() {} virtual void visit(StockItem& obj) { /* unknown StockItem not handled by derived visitor */ } virtual void visit(Widget& obj) { /* Widget visited but not handled by derived visitor */ } virtual void visit(GewGaw& obj) { /* GewGaw visited but not handled by derived visitor */ } }; class EditStockItemVisitor : public Visitor { public: void visit(StockItem& obj) { cerr << "Attempt to edit unknown StockItem " << obj.id() << endl; } void visit(Widget& obj) { /* do Widget editing here */ } void visit(GewGaw& obj); { /* do GewGaw editing here */ } }; int main() { list stockList; // fill with StockItems EditStockItemVisitor stockEditor; list::iterator pStockItem; for (pStockItem = stockList.begin(); pStockItem != stockList.end(); ++pStockItem) { (*pStockItem).accept(stockEditor); } } As you can see, when the StockItem derived object accepts the Visitor derived object, and passes itself to the visit method, the compiler will try to match the StockItem type with an appropriate visit method. If one exists, then that method will have access to all the StockItem derived class public members (and private members if you make it a friend of the class). If an exact match for the visit method is not found in the specialist Visitor, it will resolve a visit method higher up the StockItem hierarchy, finally ending up at the StockItem visitor method, where the visited object can be handled as an unknown type of StockItem. Once your StockItem classes have the accept method, you can write as many different Visitor derived types as you need, for all the types of actions you may want to perform on the StockItems. If you want a Visitor derived type that only handles some types of StockItems, only write methods in it to visit those types. Any other StockItem types you pass to it will end up in their visit method in the base class Visitor, where they can be ignored, or logged, or whatever. Using this pattern properly may require some re-designing, but with many applications it can be so successful, with Visitors everywhere, that I refer to it as 'Tourism' ;-)