Class/inheritance problem
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...
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.
That cleans things up at least somewhat.
CSceneSoldier*& pSoldier = reinterpret_cast<CSceneSoldier*&>(iterSoldier->get());pSoldier->OnStrafe(dir, strafe);pSoldier->SomethingElse();pSoldier->WhateverElse();
That cleans things up at least somewhat.
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.
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.
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.
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 ;)
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:
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.
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...
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...
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
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.
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.
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
Popular Topics
Advertisement