Archived

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

base/child classes

This topic is 6031 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

hey everybody I have this small problem here and I would really appreciate if somebody could give me a hint: I have a base class for object, lets call it CObject . This class contains it's ID, description and pointer to list of the object's attributes. Then there are its children - classes like CWeapon, CArmor, etc. These don't add anything else but a constructor that initializes the variables in CObject to proper values and functions for easier access to these variables. Then I have a storage class, a list of pointers. This is a template class, therefore it can operate on any type of pointers(CObject*, CWeapon*, CArmor*). This class should also be responsible for creating objects of those classes by calling CList::Add(new T(parameters)). Now this is the problem: I need all the children of CObject to be stored in the same list, so the list only operates on them as if they were CObject, not CArmor or CWeapon. But the constructors of CWeapon and CArmor take different parameters... How can I make it possible for the list to operate on every child class separately when they are added, but still add them to the same list so when they are retrieved, they are all of the same type(CObject)? Anyone? Oh and I should also mention that I need this to work with any number of base/child classes... well, hope that somebody will understand what I'm asking, if I read this I probably wouldn't mago Edited by - mago on June 10, 2001 9:44:11 AM

Share this post


Link to post
Share on other sites
Guest Anonymous Poster

A solution: don''t make the list the responsible entity for creating/constructing individual objects. Instead use something like:

void blacksmith() {

CSpear *ironspear = new CSpear(...);

PlayerBackPackThatIsAList.add(ironspear);

}

The add method of the list would take a CObject* for argument, since a CSpear* is also a CObject*, this works.

The problem with those generic containers is that when you get the ironspear instance out of the backpack(list), you''ll end up with a CObject* to it, and no further information. How will you figure that you took a CSpear and not some kind of CArmor out of your backpack?

There surely are elegant solutions for this, but I don''t know an ideal solution. This problem is very interesting indeed. "Quake" dealed with this by creating a single "game thing" type called "entity_t". A door in the game would be an entity as much as a player or a flying rocket or an invisible trigger. The difference is that if you wanted to know if an entity instance had some properties, you would have to inspect entity_t''s fields (wich are all the same, so the compiler takes its picky hands out of the matter) like:

someentity.health
someentity.movetype
someentity.flags (one of the bit flags was actually "is a player" or "is a monster")

One downside is that the entity_t has about 200 data fields, and most game objects use only 10% of them. For instance, the ".frags" field is used only by players to keep a track of their individual fragcounts.

I believe that making intrincated "game object" hierarchies is not a task for a C compiler (or any language''s for that matter) to handle. It''s just too dynamic and complicated.

Of course, when you let go compiler automatic typechecking, you have to implement and maintain your own typechecking. Or else players will be able to take their ironspears out of their backpacks and eat them

--
Fabio Reis Cecin
Amok Entertainment

Share this post


Link to post
Share on other sites
In other words, Quake implemented Real-Time Type Information (RTTI) with a large structure. I try to design so that I don''t have to use RTTI or similar schemes, but if I had no other way out, I would use the standard C++ RTTI before trying to implement my own. Maybe RTTI wasn''t stable/complete when Quake was written, but it''s there now so it should at least be evaluated as a solution.

As an answer to the original poster, fcecin has the right idea about constructing the object with its specific needs first, then adding it to a generic list. You might have to use RTTI to get information about the object when you grab it from the list, but there can be ways around it.

The best solution (IMHO) is to abstract objects to the point where the base class has functions that generically let you do anything you want with the object. These functions are virtual, so when you make a specific object, it calls the correct virtual function from just a base-class pointer. If you can make all of your objects work this way, you don''t need RTTI.

So you basically have 3 tools:
- keeping data and functions in the object class that are general to all objects (work exactly the same)
- generalizing object functions so that clients can call virtual functions and get the right behavior specified by the appropriate derived class
- using RTTI to get a pointer to the actual derived object, then operating on that.

You can use any combination of these you see fit. Good luck.

Share this post


Link to post
Share on other sites
Thank''s for your help everybody

It''s highly appreciated.
Combining all your posts and adding some of my own ideas I found the way...

Share this post


Link to post
Share on other sites
if your list class is global (eg a singleton) then you could put code to add/remove the object to the list into the constructor/destructor of the CObject class. This would be called automatically when you create the new object.

As for RTTI, the best way to do it is to design your classes and interfaces so you dont need it.

As for having one type fits all, there are certain advantages to this method that go beyond the problems of RTTI. With this method, your objects are almost entirely data driven. This means that if later on, you decide you want to add a GoldenSpear to your game, you can just create one in a file and load it into a standard CObject class, without the need to recompile any code.

Share this post


Link to post
Share on other sites
Hiya

I''m not positive that this is relevant to what he wants, but when I have a container that keeps a list of abstract objects, I normally create an enumeration with ID''s for all of the different classes that I am using.
enum
{
OBJECT_CLASS,
WEAPON_CLASS,
ARMOR_CLASS,
etc...
}

Then in the base class (CObject) I will have a classID variable. Now when I pull out a CObject from the list, I test the classID variable agains the enumerations and act on it depending on what it is.

void CPlayer::Use( int item )
{
GObject* obj = list.getItem( item );
if (!obj)
return;

int type = obj->getClassID();

switch( type )
{
case ARMOR_CLASS:
wearArmor( (CArmor*)obj );
break;

case WEAPON_CLASS:
sharpenWeapon( (CWeapon*)obj );
break;

default:
break;
};
}

This of course is a fictional class just to show what I mean. lol


Seeya
Krippy

Share this post


Link to post
Share on other sites
and again, that''s a cheezy C implementation of RTTI. Every time you add, remove, or change a type, you have to go through all of your code. The best way to solve this problem is (as Sandman said) avoid it altogether.

Thanks, didn''t know that about Quake--never looked at the source myself. (Besides, I haven''t liked a FPS since the Aliens Doom 2 mod).

Share this post


Link to post
Share on other sites
Carmack hates C++ and DirectX, or at least that''s what I gather from what I''ve read.

If CWeapon & CArmor do nothing different than initialize variables differently, then you did a decent job incorporating all the functionality you need into CObject - and consequentially There''s no good reason to make a CArmor or CWeapon class!

CObject* g_BaseWeapon = new CObject;
//Fill in g_BaseWeapon properties here

//later we want to make an actual weapon
COBject* RShortSword = g_BaseWeapon->Clone();
RShortSword->Property("Name")->Value = "Rusty Short Sword";
RShortSword->Property("THAC0")->Value = 20;
//etc... for different types of weapons

//If you had a container of weapons, you could randomly pick one, clone it and call it loot...
CObject* LootWeapon = RShortSword->clone();

Magmai Kai Holmlor
- The disgruntled & disillusioned

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor

Carmack hates C++ and DirectX, or at least that''s what I gather from what I''ve read.




hmm. You are pretty late for the news. Carmack said in a voodooextreme interview that Directx 8 was now good, but he would still use opengl because portability matters a lot.

And he also said that Doom 3 would be written in C++. He mentionned that he might keep the renderer in C if he can easily include all the new features in it because he liked the architecture.

Share this post


Link to post
Share on other sites
thanks for all the replies

I did something like this(of course the actual implementations have more members, private members, public accessors etc.):

class CAttribute
{
unsigned long value;
}

class CAttributeList: pubic std::list
{
unsigned long Insert(CAttribute&);
CAttribute& Find(unsigned long);
}

class CObject
{
bool valid;
int type;
CAttributeList* attributelist;

CObject(): valid(false), attributelist(new CAttributeList)
}

class CWeapon: CObject
{
CWeapon(name, damage,...)
{
InitializeAttributes();
SetAttributes();
SetType(TYPE_WEAPON)
valid = true;
};
}



so a weapon is constructed using a constructor from CWeapon, but it is registered in a list as a CObject. This way it can be in one list and when we want to see the attributes having just a CObject, we look at the type to see what to expect in the AttributeList...

mago

Share this post


Link to post
Share on other sites