Archived

This topic is now archived and is closed to further replies.

Ruval

Type checking of inherited classes?

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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 parallel

class D2 : public B { }; // in inheritence graph


void f (D1 *d) { } // function overloads for

void f (D2 *d) { } // each derived class, NOT base class


int 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.

Share this post


Link to post
Share on other sites
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 :P

Derived *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.

Share this post


Link to post
Share on other sites