Type checking of inherited classes?

Started by
5 comments, last by Ruval 22 years, 9 months ago
Hi, just a little C++ question that isn''t covered in any book I''ve ever read... I had this idea for a collision class, where each type inherits from an abstract base. So, you''d have a list of Collision objects that could be Line, Circle, Box or Point. The clever (maybe) bit is that each one has a set of virtual functions for checking against each other type, like class Collision { virtual bool Collision::Collide( Line & ) = 0; virtual bool Collision::Collide( Circle & ) = 0; }; class Circle : public Collision { virtual bool Circle::Collide( Line & ) {/*implementation*/}; virtual bool Circle::Collide( Circle & ) {/*implementation*/}; }; But.... with the list of base class pointers, if I called Collision *a, *b; // Where ''a'' is a Line and ''b'' is a Circle if( a->Collide(*b) ) would the correct type conversion to the inherited class be done?
Advertisement
Yes.

---- --- -- -
Blue programmer needs food badly. Blue programmer is about to die!
Yes, but... casting down is always safe, casting up can be dangerous. The pointers have to "trust" you to know the type.

Generally when I use generic base classes I include an enum member that specifies type in the base class, and set the enum in the derived constructor. That way I can check actual type on the other side of things like messages (during which the pointer is passed as an LPARAM) without getting into trouble, because the enum member will tell me what the original type was, which allows me to cast up without any danger of casting to the wrong type.

-fel
~ The opinions stated by this individual are the opinions of this individual and not the opinions of her company, any organization she might be part of, her parrot, or anyone else. ~
I had thought of having enums and them as indices into a table of function pointers, but that''s not nearly as elegant

Thanks for the reassurance, now I just have to find some time to invest in coding the thing!
Well, the bit I thought would be problematic, was.

The tricky part wasn''t the function call itself but the parameter, which doesn''t get correctly translated to the derived type. So if I call

Collision *a, *b; // Where ''a'' is a Line and ''b'' is a Circle
a.Collide(b);

I get a compile message that type Collision* could not be
cast to type CollisionPoint*.

Does anyone know of a way to make this work or am I stuck with a switch statement for casting?
After re-reading Ruval''s original question, I think the answer is that it can''t be done. Here''s what he''s wanting in general terms:
  class B { };class D1 : public B { }; // D1 and D2 are parallelclass D2 : public B { }; // in inheritence graphvoid f (D1 *d) { } // function overloads forvoid f (D2 *d) { } // each derived class, NOT base classint main (){    D1 d1;    D2 d2;    B *pd1 = &d1 // base pointers to objects of type D1    B *pd2 = &d2 // and D2    f (pd1); // error -- no overloads can convert from B*    f (pd2); // ditto    return 0;}  


This won''t work because the compiler knows that D1 is-a B, but doesn''t know if B is-a D1 or D2.

This seems quite similar to the question I asked many moons ago, but never got a good solution: how do you overload a function so that two parties will dynamically interract with each other correctly.

My question was specifically related to the RTS-type method where units have a "default action", so that when you click on a target, the selected unit will do different things depending on what kind of target it was. I envisioned a virtual Unit::whenclickedon (Unit &target), but that requires dynamically resolving what the selected (this) unit is in addition to the target.

Ruval''s question is the same, but a different instance: how would you write virtual Object::Collide (Object &target), given that the behavior depends on both the concrete type of the object (this) and its target?

I don''t have a good answer, but it''s a very good question. If you need to use some kind of type identification to resolve this issue in a switch (I don''t like that solution either, but I have no other ideas), I would suggest using C++''s built-in RTTI (Run-Time Type Information). You have to turn the switch on in the compiler for MSVC, but after that you may use dynamic_cast to try to divine what kind of base class object you really have.

Another idea, and I think the better way to do it, is to take the kinds of things you need to know when figuring a collision and abstract them into a top-level interface. It''s hard to show an example with collisions, but it''s easy with moving:

Let''s say you have a class "Vehicle", and you want to move the vehicle. You might think you would like to have a Vehicle::move command, but what are the arguments? A boat moves differently than a car or a plane, and you wouldn''t be able to tell what is what from a base class. A different solution would be to have methods like "getSpeed" and "getAltitude" that could be overridden for each derived class generically, then the engine running the base class wouldn''t need to know what''s what.

If you could find the analog of "getSpeed" for your collisions, you might have a better solution there.
You could always force the compiler to convert from a base type to a derived type by using static_cast and dynamic_cast:

  Base *b;  // Could point to a derived type or a base type; ahh the wonders of polymorphism :PDerived *d = static_cast<Derived*>b;  


Of course, you would want to use dynamic_class for base-to-derived, since it performs dynamic checking to make sure the Base pointer actually points to the Derived type, and returns NULL otherwise. static_cast just trusts the programmer, and could be very bad if used at the wrong time.

This topic is closed to new replies.

Advertisement