Class/inheritance problem

Started by
20 comments, last by Programmer16 19 years, 8 months ago
I've got following classes in my program: CSceneObject |-CSceneCamera |-CScene......(particles,bullets etc) |-CSceneCharacter ```|-CSceneZombie_Base `````|-CSceneZombie_TypeXXXXX `````|-CSceneZombie_TypeYYYYY ```|-CSceneSoldier My problem is that some of the classes have unique methods(for example CSceneSoldier have 'OnStrafe' and other classes don't). Now - In a number of places in the code I need to call these unique methods. So I check the type of the object(I implementend pure virtual get_type() function in CSceneObject) and for example if it's soldier then I call: ((CSceneSoldier*)(*iterSoldier).get())->OnStrafe( dir, strafe ); It's so complex because I'm using STL::list and boost::shared_ptr, so first I need to dereference iterator, then get raw pointer and then cast it to proper type. But it's making my code a piece of sh.. so I figured out that I can put all these unique methods ito CSceneObject and make them throwing an exception or asserting by default, then override them in the proper class so I will avoid casting and assure that all calls will be valid. What do you think about that? Or maybe there's know solution to this problem? I'm confused how to do it in more clear way...
www.tmreality.com
Advertisement
There may be a better way, but I typically go with your first way. If I'm going to be making multiple calls on the child pointer, however, I tend to make a reference and use that.
CSceneSoldier*& pSoldier = reinterpret_cast<CSceneSoldier*&>(iterSoldier->get());pSoldier->OnStrafe(dir, strafe);pSoldier->SomethingElse();pSoldier->WhateverElse();

That cleans things up at least somewhat.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
The idea behind virtual functions (which is what I think you're probably using now) is that you don't need to know the type because it is handled behind the scenes. HOWEVER, having lots of objects with 'null implementations' (that just throw) sounds like a very bad idea.

The idea is that when you're working with CSceneObjects you only operate on CSceneObjects. If you need to access data in CSceneSoldier from the code processing CSceneObjects, then you're doing something wrong probably.

I can't really say how to fix the design without a lot more information, but I'm not good at design anyways so I don't really know what info I'd need to make a decision.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
To make life a little easier for you, look at boost::dynamic_pointer_cast, which is built for just such an occaision. It'll make the code a little cleaner.

However, really take a look at how you do your object processing - you shouldn't need to do any downcasting, but it may be the simplest/quickest way to do it.
I never use that approach, because I kinda consider it defeats the purpose of using virtual functions and base classes in the first place.

When an object has a property that its parent does not have, it either:

- Manages this property on its own (from the member functions it inherited from its parent) or
- Is added to another container as well, that knows its type and can then work on its properties

That is, if you store "soldier" as "scene object" somewhere, never assume it is a soldier (or even think about it). If you want to work with soldiers, get a soldier list, don't use the scene object list for that, you pervert ;)

You already have SceneCharacter, so why not implement all the virtual onStrafe, onRun, onBite, onEtc functions in there.

This way, you'd have a class like:

class ISceneCharacter : public ISceneObject{public:  virtual int onRun() { return 0; }  virtual int onBite() { return 0; }  virtual int onStrafe() { return 0; }};class SceneSoldier{public:  int onRun() { // override & implement run }  // No bite method, don't override  int onStrafe() { // override strafe }};class SceneZombie{public:  // No run  int onBite() { // override and implement bite }  // no strafe};


That's one way I see of doing it. Adding specific character methods to the character class allows you to customise these types without casting. It'd make little sense adding them to SceneObject as bullets, particles etc can't run, bite or strafe.
Agony => txh, your way of doing this is visually much cleaner

Extrarius => Yes, I know that it isn't good idea, but at least I would avoid casts... My design is far from clean but I couldn't think any ways of imroving it. Besides my game is to be shipped soon - everything works fine and is very stable, but I if I'm using stl containers, shared ptr's, memory manager, exceptions and other things that protects me from pointer hell I would like to avoid casts that can ruin all these guards

ze_jackal => I've got to see it, it's the first time I've heard about dynamic_pointer_cast. Thx in advance! Refering to my design - yes I know that - I was thinking about virtual function CSceneObject::SendMessage derived in child classes, but I'm passing many different things that it would mess my code more than casting - I would need either to pass pointers(instead of this I prefer casting) or make another class hierarchy only to pass different messages...

www.tmreality.com
I agree with ToohrVyk above but if you must go your way then you must. The problem is described by Gamma et al in their book so you're not alone on the path you've chosen.

The path forks in the book to the solution Evolution mentioned above and to the following:

class CSceneObject {
public:
..
virtual CSceneSoldier *GetSceneSoldier() { return NULL; }
..
}

class CSceneSoldier {
public:
..
virtual CSceneSoldier *GetSceneSoldier() { return this; }
..
}

if (test=SceneObject->GetSceneSoldier()) {
test->strafe();
}

JD
evolutional => you're right, but it won't allow to avoi catsing, because instead of CSceneObject->CSceneSoldier I will have CSceneObject->CSceneCharacter, so it's just 'swapping bottlenecks'?

ToohrVyk => I had a similar solution, but it was hard to manage - maintaining a number of connected lists.

After ToohrVyk's post I've come up to some idea. What about traversing on each frame SceneObject's list and making list of zombies, list of soldiers etc only for this one frame. It wouldn;t prevent casting but it would make it much more error-prone due to focusing all casts in one place?


[EDIT]

JesusDesoles => What should I say... It's a kind of brilliant trick - very easy and very helpful, error prone. BIG THX! I don't know what approach I will use but your solution is now my favorite.
www.tmreality.com
Quote:Original post by tomek_zielinski
evolutional => you're right, but it won't allow to avoi catsing, because instead of CSceneObject->CSceneSoldier I will have CSceneObject->CSceneCharacter, so it's just 'swapping bottlenecks'?


True, but the idea is that you don't have to cast - you don't have to know if the Character is a soldier or a Zombie, it just has to know it's a character. Of course, you'll still get virtual functions (which may be the bottleneck you're talking about?). Then again, in my system I keep lists of (eg) Characters, not just the objects so I don't need to cast. I don't seem to have any problems with such a setup, but then again my requirements are probably lower or different than yours. I'll watch this thread to see if anyone posts anything interesting to this topic because it's probably something I'll need in the future.

This topic is closed to new replies.

Advertisement