Path: utzoo!utgpu!news-server.csri.toronto.edu!torsqnt!geac!alias!fred!rae From: rae@gpu.utcs.toronto.edu (Reid Ellis) Newsgroups: comp.lang.c++ Subject: Re: Multiple inheritance and type casting Message-ID: Date: 10 Oct 90 04:38:49 GMT References: <5087@uqcspe.cs.uq.oz.au> <2709eabf.5abd@petunia.CalPoly.EDU> <5130@uqcspe.cs.uq.oz.au> Sender: Reid Ellis Reply-To: Reid Ellis Organization: Alias Research, Inc. Toronto ON Canada Lines: 81 Reid Ellis writes: |I have found that casts lead to errors, plain and simple. If |you are casting base types to derived types, something is |wrong with your design. Iain Fogg writes: |I don't agree. Take for example the overloading of |operator==. If we defined it as follows | inline operator == (Base &b1, Base &b2) { return b1.IsEqual (b2); } |where IsEqual is a virtual function declared in class Base. |It's prototype is | virtual int IsEqual (Base &); |Now, any class derived from Base will probably want it's own |version of IsEqual, but because the formal parameter is a |Base &, a cast to the derived class will be required to |compare attributes of the derived class (for example). | |For example, | class Derived: public Base { | public: | virtual int IsEqual(Base &b) { return x == ((Derived &) b).x; } | private: | int x; | } |We can then say things like | | Derived d1, d2; | | if ( d1 == d2 ) ... But how can it be *known* that the operand of operator==() is a Derived? One could equally well say something like: Derived d1; Base b; if(d1 == b) ... and the above operator==() would fail, or at least return a meaningless value since the operand is not a Derived. This is what is meant by "dangerous". It's even more so if pointers are being dealt with rather than ints. The closest "safe" method of accomplishing the above is to define the following methods in base: virtual int Base::IsEqual(Base &b); virtual int Base::IsEqual(Derived &d) { panic("Attempt to compare a Base and a Derived\n"); } int Derived::IsEqual(Base &b) { return b.isEqual(*this); } int Derived::IsEqual(Derived &d) { return x == d.x; } Now obviously this is not always possible since modifying the base class may not be an option. This leads to things like the NIH library's "static char *name" and "virtual char * getName()" type of functionality for each class to identify itself. [The actual code is different, but the idea is basically as represented] Then you can write code as follows: int Derived::IsEqual(Base &b) { // Check for same type first if(b.getTypeName() == getTypeName()) { // Okay, it's really a Derived return ((Derived &)b).x == x; } else { panic("Derived::IsEqual wrong type", b.getTypeName()); } It is ugly, but it checks for the correct type before doing anything dangerous. Real code would have to be more complicated than the above since it would have to handle descendants of Derived as well. It comes down to the fact that if two base pointers are being compared in this manner, where derived class data is required to determine the result, the comparison is being done at the wrong level. Somewhere type information is being lost where it needn't be. Reid -- Reid Ellis 264 Broadway Avenue, Toronto ON, M4P 1V9 Canada rae@gpu.utcs.toronto.edu || rae%alias@csri.toronto.edu || +1 416 487 1383