Archived

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

Polymorphism and Class Crazyness!!

This topic is 5676 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Okay here''s my idea... i want a texture manager that can handle textures that are of different formats... i''ve created a Texture super class that all other textures are derived from, say for example CTextureBMP or CTextureJPG. Essentially i want to be able to have an array or map or vector or something of these... Now i know it is possible to say CTexture *tex = new CTextureBMP(blah blah); but when i do this some methods in the derived class are not available, and to make them available i have to make virtual methods in the super class, which arent necessarily used by all the different textures... Now, what i''m thinking is that maybe i can cast the pointer to the actual class that it is, like CTextureBMP *tex2 = dynamic_cast< CTextureBMP * > (tex); is this possible? Is there a better way to do this? How would you do it? thanks -jonnii

Share this post


Link to post
Share on other sites
First off let me say that casting down is really dangerous. Disclaimer, yadda.

That being stated... I've done this, by putting an enumerator in my base class that basically records, at time of creation, which type that particular object was created as (which derived class). This allowed me to create a seriously complex messaging system between Windows and UNIX, Windows and Windows, and UNIX and UNIX in which everything was passed as the base type that knew what it was supposed to be.

A variation on this would be to record as a string the name of the class the object originally belonged to, which I believe is how CObject does it (I had a finite number of object types so I didn't think that was necessary.)

This way, you can even slam the thing through a void* without losing track of what you were (assuming, of course, that all your void* operations are guaranteed to be one of your base objects).

-fel

[edited by - felisandria on June 3, 2002 7:10:22 PM]

Share this post


Link to post
Share on other sites
The operation to use is static_cast:
CTextureBMP *pTexBmp = static_cast
(pPointerToCTextureThatImPositiveIsReallyACTextureBmp);

Alternatively, you can turn on RTTI and use dynamic_cast with the same syntax, except dynamic_cast will return NULL if it really _wasn''t_ a CTextureBmp, but is a whole lot slower than static_cast. Plus there''s the overhead of RTTI, yadda yadda.

Share this post


Link to post
Share on other sites
I really think that your first aproach was correct. That kind of thing is why polymorphism was created for.

Let C++ do for you and use only pointers to the parent class, declare the methods that can change as virtual, make the parent class abstract ( with a "= 0" in a virtual method) if you are not instantiating it.

Then, with your pointer to texture (parent) call the function that you want. Magically will resolve in the appropiate child class method.

Real programers are not afraid from maths!
(from an Asfixia Member I think)
JJpRiVaTe (Private Zone)

Share this post


Link to post
Share on other sites
I solved this finally by reorganising my texture classes to accomodate what functions needed to be in scope. It turned very nicely...

thanks anyway!!

-jonnii

Share this post


Link to post
Share on other sites
quote:
Original post by felisandria
First off let me say that casting down is really dangerous. Disclaimer, yadda.

No it isn''t. He suggested using dynamic_cast, which is alway safe -- it throws if a bad cast to a reference type is attempted, it returns NULL if a bad cast to a pointer type is attempted.

Share this post


Link to post
Share on other sites
Can''t help much with your question, but maybe a different approach would be appropriate? Perhaps have a class that will load all formats, with separate methods to load each format. Your texture class will have a single instance of the loader class. The loader will get all data from the file and place it in a universal format for use later in your code. Just a thought.... Dunno how it''d work for you though.

Share this post


Link to post
Share on other sites
quote:

which is alway safe -- it throws if a bad cast to a reference type is attempted, it returns NULL if a bad cast to a pointer type is attempted.


I suppose that depends on how you define "safe"... *wry smile*

Personally I consider it much safer to have an "IsKindOf" type operation to switch off of if you plan to do an awful lot of casting down. It''s much more useful than trying a hit-and-miss and hoping it doesn''t throw. To me, a throw is not the result of what I would consider a "safe" operation.

-fel

Share this post


Link to post
Share on other sites
The only reason dynamic_cast throws an exception with references is because it has to - you can''t have an unbound reference.

Switch on IsKindOf() is icky - its case analysis based on type and should be avoided.

Case analysis by type is a poor-man''s polymorphism - so it should just use virtual functions

Admittedly sometimes you just can''t avoid downcasting as you can''t create an uber-interface class/abstract class.

Of course, an alternative to that may be the Visitor pattern.

dynamic cast is ALWAYS better than a home-grown IsKindOf() - its enforced by the compiler and you won''t have to worry about accidentally returning the wrong string or int constant - which would then blow up.

I think one of the lamest things VC++ 6.x does is have RTTI OFF by default for new projects - that''s not the ANSI Standard!

RTTI is definately worth the overhead - very few cases of things should need RTTI turned off. You probably actually get more speed improvement from turning off exceptions than turning RTTI off.

But unless you really know you need to, those two language features are very useful and I''d recommend turning them on.

Share this post


Link to post
Share on other sites
IsKindOf-style switching works better than dynamic cast in cases where you are *required* to cast all the way down to essentially a void*, such as in cases like the UNIX/Windows messaging system I described (in which case recognition of object class type is frankly the simplest thing you can do to yank it back out). So long as your enums are complete, or if you build in even the slightest bit of robustness into a "homegrown" IsKindOf style string-based system, you will be FINE.. without the overhead that dynamic casting drags in with it... not to mention the inherent sorting abilities it gives you if you''re throwing objects across a messaging system and then sub-filtering them into the correct types to place in a data structure of some sort (personally I was using a rather large heirarchy tree in which object type also indicated storage level.)

The fact that some people do stupid things when setting up homegrown IsKindOf scenarios doesn''t mean that they should be dismissed entirely as unstable. Code is always as stable as you make it, regardless of whether you''re using generic compiler-supported constructs.

-fel

Share this post


Link to post
Share on other sites
The point I was trying to make is that using built-in language supported features is better and safer in MOST cases - you do have the one example where it is not.

An alternative way to handle the kind of example you describe may be a pluggable factory.

The problem with case analysis is that if it ends up exploding over the code base and you have to add a new class type - that case analysis stuff has to be hunted down and dealt with - which IS error prone. I've seen many examples of bugs in code at my work related to this kind of problem.

If your example/use case has all the case analysis centralized, then it avoids that problem.

Another alternative solution to the kind of thing you describe is the Prototype Heirarchy - from the Lakos book Large Scale C++ Software Design - its the classic persistence problem - you just happen to be streaming the objects.

Granted, in those cases you can't really rely on RTTI to do your persisted object type determination - but you can still avoid a lot of case analysis using factory methods et.al.

Also, there is the type_info class, which the typeid operator returns - which has a method - name() which returns a human-readable string for the type - this type could be used to do sorts - I have done so with a good deal of success.

I'm not saying there aren't situations where case analysis and homegrown object ID-ing isn't the correct solution - its just not a common case. The original example is one where case analysis is not appropriate.

[edited by - SteveC on June 4, 2002 7:42:31 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by felisandria
I suppose that depends on how you define "safe"... *wry smile*

"safe" as in "it doesn''t let you do the wrong thing".

quote:
Personally I consider it much safer to have an "IsKindOf" type operation to switch off of if you plan to do an awful lot of casting down. It''s much more useful than trying a hit-and-miss and hoping it doesn''t throw. To me, a throw is not the result of what I would consider a "safe" operation.

Then use pointers instead of references. The only reason it throws is because you can''t have a null reference.

Share this post


Link to post
Share on other sites
Im a newbie and I haven''t gotten to test this yet but here is how I plan to work this I my texture class (if there is something wrong with this please let me know). It will have one public load function and a bunch of private functions depending on the file type. The public function will StrPos() for .bmp, .tga etc and the call the appropriate function.

Polymorphism is an end, you don''t necessarily need to use inheritance to get there. If it creates more work, is unsafe or you need to know what you are dealing with before hand then it defeats the purpose.

Share this post


Link to post
Share on other sites