• Advertisement

Archived

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

how to tell what class an object is in c++

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

let''s say i have a virtual base class, CThing, from which is derived CItem, CWeapon, CArmor, et cetera... some other classes are derived from these derived classes as well. i use a pointer to the base class for an inventory listing (as all the specific details aren''t important, i just want a quick listing). is there a way to tell what derived class those objects belong to from there? right now i am considering storing a char member variable which i can check (0 = CBase, 1 = CItem, 2 = CWeapon, et cetera)... or maybe a virtual function that returns the type (also more or less returning a char, but overridden in the derived classes)... then i can check this number and take appropriate action (i.e. if this object has a 2 (weapon), cast it to CWeapon and grab it''s damage stat). is there a nice function or something so i can put:
CWeapon DaWeapon;
CBase* DaItem = &DaWeapon;
ClassType = FetchClass(DaItem);
 
or something to that effect?

Share this post


Link to post
Share on other sites
Advertisement
Look into C++ RTTI ( Run-Time Type Information ).

"Doomed to crumble, unless we grow, and strengthen our communication" - Maynard James Keenan, Tool

Share this post


Link to post
Share on other sites
quote:
Original post by krez
let''s say i have a virtual base class, CThing, from which is derived CItem, CWeapon, CArmor, et cetera... some other classes are derived from these derived classes as well. i use a pointer to the base class for an inventory listing (as all the specific details aren''t important, i just want a quick listing). is there a way to tell what derived class those objects belong to from there?


Rooting a class hierarchy at something with a name like "CThing" is rarely a good idea. It bypasses the type-safety system and violates LSP.

quote:

right now i am considering storing a char member variable which i can check (0 = CBase, 1 = CItem, 2 = CWeapon, et cetera)... or maybe a virtual function that returns the type (also more or less returning a char, but overridden in the derived classes)... then i can check this number and take appropriate action (i.e. if this object has a 2 (weapon), cast it to CWeapon and grab it''s damage stat).


If you were to adhere to LSP then you would not need to do this, you would prefer to use the builtin runtime polymorphism facilities of C++. I''d recommend that you look at refactoring your design to remove the need to manually discriminate on type.

If you are hell-bent on doing this, then you need to use the dynamic_cast<> facility to perform dynamic downcast. Look it up in your favourite C++ book.

Share this post


Link to post
Share on other sites
you should elaborate on what your trying to do, because it doesnt make alot of since.
you can get the type like this:
  
class C
{
public:
const type_info& getType() {return typeid(this); }

};
int main(int argc, char* argv[])
{
C c;
cout << c.getType().name;
return 0;
}

however, imo, typeid is really, really, crappy.

generally, when you use virtual, you dont need to worry about the type since the compiler will make virtual tables.

if you are putting many unrelated derived classes in a single array (why?), what you can do is use a general accessor to get the item to do what you want, assuming that it has been initialized.
  
class Cbase {
public:
virtual void act() = 0;
};
class Cweapon: public Cbase{
int dmg;
public:
void act();
};
class Cdude: public Cbase{
int hp;
public:
void act();
};

in this example, i can call act no matter how unrelated the dobob is from the other dobobs, without worry.

IF YOU THOUGHT IT OUT, then, for a base like Citem, you could have a function called effect(Cbase* object), and you wouldnt need to know the Citem type or Cbase type.
  
class Citem{
public:
virtual int effect(Cbase* object) = 0;
};
class Cweapon: Citem{
int _dmg;
public:
int effect(Cbase* object) {
object->hp = object->hp - ( _dmg - object->ar);
}
};


workable?

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan
Rooting a class hierarchy at something with a name like "CThing" is rarely a good idea. It bypasses the type-safety system and violates LSP.

er... i don''t get you on this one... how would the name of my class violate anything? what is LSP?
quote:
If you were to adhere to LSP then you would not need to do this, you would prefer to use the builtin runtime polymorphism facilities of C++. I''d recommend that you look at refactoring your design to remove the need to manually discriminate on type.

maybe i will adhere to LSP when i find out what it is... please don''t think i''m being lazy, google told me LSP was six million different things, and Louisiana State Police was as good a match as any... but i DO need to know the type... say you can right-click on any inventory item (an array of CThings, which holds the name and icon of the item) and get a pop-up menu of what you can do with it. a potion could be drank, a weapon can be equipped, and other red herring items have no actual use. i suppose i could put all sorts of stuff into the base class (an array of menu items, et cetera) to track this, but i''d rather get it from the derived classes and then use the functions there.
but anyways, thanks for your condescension.
EvilCrap: they are not completely unrelated derived classes, they are an array of items in the inventory (all are derived one way or another from CThing). so, i can list them all as if they were just CThings, but i can also grab one and use it''s class-specific functions (the base class doesn''t have an equip() function like the weapons and armor do) by re-casting it. i don''t want to use something like the effect() method you mentioned, because some of them will have one useful function, and others might have many.
daerid: i will google that one...

--- krez (krezisback@aol.com)

Share this post


Link to post
Share on other sites
quote:
Original post by krez
er... i don''t get you on this one... how would the name of my class violate anything? what is LSP?


It''s not the name of the class, it''s what the name makes me think you are doing with it. It''s similar to CObject in MFC, and it implies a deep hierarchy with a root class containing very few semantics. What you have described you are doing is little more than using the root to give you a placeholder - it''s not really any better than casting to void*. LSP is Liskov Substitution Principle, and is a fundamental principle of OO.

quote:

maybe i will adhere to LSP when i find out what it is... please don''t think i''m being lazy, google told me LSP was six million different things, and Louisiana State Police was as good a match as any...


I assumed that you would have heard of it. I have to make some assumptions to be able to communicate with you.

quote:

but i DO need to know the type... say you can right-click on any inventory item (an array of CThings, which holds the name and icon of the item) and get a pop-up menu of what you can do with it. a potion could be drank, a weapon can be equipped, and other red herring items have no actual use. i suppose i could put all sorts of stuff into the base class (an array of menu items, et cetera) to track this, but i''d rather get it from the derived classes and then use the functions there.



How you design is up to you, I was offering some hints on things you are doing which experienced designers avoid.

quote:

but anyways, thanks for your condescension.


What is it about certain people that they come to a public forum asking for help, and they are simply unable to accept advice graciously? I have not at all been condescending. In fact, I assumed that you had more knowledge than I now think you do. It''s often difficult to judge how much someone knows from a single post, and I had to make some assumptions about what I thought you might know. So, in taking time out of my own day to try and offer help, I''ve simply discovered that you have a chip on your shoulder.

You might also like to consider that I actually gave you the answer to your immediate question, but you seem to have ignored that simple fact.

--

It is against the law to stare at the mayor of Paris.

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan
It''s not the name of the class, it''s what the name makes me think you are doing with it. It''s similar to CObject in MFC, and it implies a deep hierarchy with a root class containing very few semantics. What you have described you are doing is little more than using the root to give you a placeholder - it''s not really any better than casting to void*. LSP is Liskov Substitution Principle, and is a fundamental principle of OO.

ah... ok then. the base class holds information that is common to all the derived classes, and a few virtual functions that are overridden when necessary. what principle is it that makes this just as bad as using void*? having looked up LSP (thanks for the full spelling), i don''t see how i have violated it (of course, i only just read about it whereas you have been studying it for years).
quote:
What is it about certain people that they come to a public forum asking for help, and they are simply unable to accept advice graciously? I have not at all been condescending.

except for:
quote:
I assumed that you would have heard of it. I have to make some assumptions to be able to communicate with you.

quote:
How you design is up to you, I was offering some hints on things you are doing which experienced designers avoid.

quote:
In fact, I assumed that you had more knowledge than I now think you do.

my apologies for seeming ungracious... i do tend to get snippy when i ask a specific question and someone takes it upon themselves to degrade my design rather than just answering. but, since the subject has been brought up, how exactly would you implement the system i was talking about (storing an array of pointers to a base class, and being able to pick one and call a specific function in it)?
quote:
You might also like to consider that I actually gave you the answer to your immediate question, but you seem to have ignored that simple fact.

if you consider "you are doing it all wrong, but if you are hell-bent... look it up in your favorite c++ book" an answer.

Share this post


Link to post
Share on other sites
I don''t know if this would work but couldn''t you make a string that could tell what type it is? If you are making a weapon set that string to WEAPON. Then when you want to see what type it is just make a function that retrieves it. Thats what I would try.

Jeff D

Share this post


Link to post
Share on other sites

#include <iostream>
#include <typeinfo>
using namespace std;

class Base
{
public:
//code...
};

class Derived : class Base
{
public:
//code...
};

class Derived2 : class Base
{
public:
//code
};
int main()
{
Base *p,b;
Derived d;

p = &Derived;
.
.
p = &Derived2;
.
.

cout << "the type of object p is pointing to "
<< typeid(*p).name() << "\n";

return 0;
}



-----------------------------
"There are ones that say they can and there are those who actually do."

"...u can not learn programming in a class, you have to learn it on your own."



Edited by - cMADsc on February 25, 2002 5:58:31 PM

Share this post


Link to post
Share on other sites
storing the items as the baseclass is your problem. there is no good way to overcome your problem without really defeating the purpose of an object-oriented design. you need to design your base class so that all derived classes have the same interface (variables and functions). and yes some of that info may not be relevant on all classes, but that''s a much easier problem to solve than the one you''re facing right now. that''s how i would do it anyway.

Share this post


Link to post
Share on other sites
Look, public inheritence implies an "is-a" relationship. I will describe a scenario detailing its intended use, and then a scenario detailing its retarded use:

Intended use:
You can use a derived class as if it "is-a" base class.
consider:
  
Class Weapon
{
enum DamageType {NONE, PROJECTILE, EXPLOSIVE, PHYSICAL};
virtual float GetDamage()=0;
virtual float draw()=0;
virtual DamageType GetDamageType()=0;
};

class RocketLauncher:public Weapon
{
virtual float GetDamage() { return 15; }
virtual float draw(); //you can do this one ;)

virtual DamageType GetDamageType() { return EXPLOSIVE; }
};

class MachineGun: public Weapon
{
virtual float GetDamage();
virtual float draw();
virtual DamageType GetDamageType() { return PROJECTILE; }
};

This is not a particularly extensive example, but the idea is that any derived class "is-a" base class, and you treat them all the same. I can treat MachineGun and RocketLauncher both as weapons, and call any virtual functions that make sense for Weapon, as they make sense for MachineGun and RocketLauncher as well.
If you can''t find common functionality(as in your case), or you don''t use your base class for anything, you shouldn''t derive from that base class.

p.s. It is recommended by some c++ experts that every class in a heirarchy tree that is not a leaf should be made abstract(i.e. contains a pure virtual function) so it cannot be instantiated.
Now here is a horrible example using inheritance:
  

class CBase
{
enum Class { Base, Int, String};
virtual Class GetClass();
};

class CInt:public CBase
{
int rep;
int GetInt() { return rep; }
Class GetClass() { return Int; }
};

class CString:public CBase
{
string rep;
string GetString() { return rep; }
Class GetClass() { return String; }
};


and then using it like this:
vector<CBase*> poly_array; //polymorphic array

poly_array.push_back(new CInt(5));
poly_array.push_back(new CString("lala"));

//print the array

for(int i=0;i<poly_array.size();i++)
{
switch(array[i]->GetClass())
{
case Int:
CInt* p=dynamic_cast<CInt*>(array[i]);
cout<<p->GetInt()<<endl;
break;
case String:
CString* p=dynamic_cast<CString*>(array[i]);
cout<<p->GetString()<<endl;
break;
}
}

Never, ever, ever program like this. This is a horrible misuse of the whole concept of inheritence. If you find yourself writing code like this, you are doing something wrong. You can redesign your program to actually benefit from inheretence, rather than having it just for kicks.

Share this post


Link to post
Share on other sites
quote:
Original post by krez
if you consider "you are doing it all wrong, but if you are hell-bent... look it up in your favorite c++ book" an answer.



No, but I do consider "you need to use the dynamic_cast<> facility... look it up in your favourite C++ book" to be a reasonable answer.



--

It is against the law to stare at the mayor of Paris.

Share this post


Link to post
Share on other sites
Fragmo: i am not storing them as the base class... but for my inventory management stuff i have a base-class pointer to all those various derived-class objects, so they can all be listed. then, iff a particular element is selected i want to find out if it is a weapon or a potion, or whatever, and give the player options to use class-specific functions (weapon->equip, potion->drink, whatever).
sjelkjd: yah... but CWeapon/CPotion/CArmor "is-a" CThing... is it really improper to add additional functions/variables to a derived class which don''t exist in the base class? these new members (equip(), drink(), etc()) do not break their CThing-ness... but there is no reason to have an equip() function (even a virtual one) in six different classes when only one of them uses it. i thought this was the point of OOP...
i think i suck at explaining myself

Share this post


Link to post
Share on other sites
krez, there''s nothing syntactically wrong with it, but what does CThing provide? Nothing! You shouldn''t derive if you''re not going to use shared functionality.

Otherwise you''re obfuscating your class heirarchy for no good reason.

Share this post


Link to post
Share on other sites
Here is a look at this from another angle. The point of creating classes B and C both derived from class A is so that, when you have a block of code that operates on an object of type A, you can actually pass it an object of type B or type C and it will never have to know the difference. Notice the "NEVER HAVE TO KNOW" part. If your code has to say "if this is actually a type B object, then I should do this to it, but if its type C, then I should do this", then you violate this principle. From an OOP standpoint, the main problem with doing this is that you have to add specific code for each different derived class type.

Now that I have said you should never do this, I''ll go on to say that I do this from time to time. I consider it an academic vs. real world issue. There are cases where you should never do this (for instance, never ever do this if you are writing a reusable library). However, there are cases where I see this as acceptable. Sometimes it just works out to be the fastest solutions...and in the real world, its often all about getting things done and getting them done fast.

That said, you have to be careful where to apply it, because it can come back and bite you in the ass later (and take twice as long as if you had done it the true OOP way the first time). So, I would generally discourage you from doing this, but I wont tell you not to do it.

Share this post


Link to post
Share on other sites
quote:
Original post by LordKronos
Here is a look at this from another angle. The point of creating classes B and C both derived from class A is so that, when you have a block of code that operates on an object of type A, you can actually pass it an object of type B or type C and it will never have to know the difference. Notice the "NEVER HAVE TO KNOW" part. If your code has to say "if this is actually a type B object, then I should do this to it, but if its type C, then I should do this", then you violate this principle.


Nicely said. You''ve basically distilled the essence of why this is a violation of LSP. LSP is intended to help with encapsulation, where you don''t need to go casting objects around to get at their particular interface specifics. If they are being used in the context of a particular base object, then access via that base should be sufficient to do what is needed.

--

It is against the law to stare at the mayor of Paris.

Share this post


Link to post
Share on other sites
I think I am the first to say that your design is fine. I would probably do it the same way...
I have three solutions for you.
*Use RTTI (which was mentioned by daerid). Look it up in MSDN or whatever.
*Store your own type variable in the base class (which you have already considered). This is essentially what RTTI will do for you.
* Add some more virtual functions to accommodate to those "you violate LSP / OOP / whatever" people.
Since I don''t know exactly how your system works I can only give some examples.
Let''s say the user right-klick on an item. You can have a virtual function in your base class that builds a context-menu. If it''s a weapon, its overided function will add "EQUIP" to the context menu. If it''s a potion, its overrided function will add "DRINK" to the context menu.
Another example would be to consider equip and drink the same type of action and add a virtual funcion called Use() or something to you base class.
If Use() is called on a potion it will make the player drink it.
If Use() is called on a weapon/armour, it will make the player equip it.

I hope I was of some help.

Cheers!

/ Martin

Share this post


Link to post
Share on other sites
quote:
Original post by krez
ah... ok then. the base class holds information that is common to all the derived classes, and a few virtual functions that are overridden when necessary. what principle is it that makes this just as bad as using void*? having looked up LSP (thanks for the full spelling), i don''t see how i have violated it (of course, i only just read about it whereas you have been studying it for years).

By having everything in your application derive from a common base, you are degrading the semantics of CThing so that it captures very little behaviour. The purpose of a base class is to capture commonality of behaviour, yet I''m very skeptical that everything that could be described as a "thing" has much in common.

You''ve already explained that you think you need to have an object identity discovery system, with gratuitous casts down the hierarchy to get at the interface of the real object. The purpose of polymorphism is partly to eliminate long switch or if-else chains, as history has proven them to be a maintenance nightmare.

quote:

my apologies for seeming ungracious... i do tend to get snippy when i ask a specific question and someone takes it upon themselves to degrade my design rather than just answering. but, since the subject has been brought up, how exactly would you implement the system i was talking about (storing an array of pointers to a base class, and being able to pick one and call a specific function in it)?



I would likely define abstract interfaces for each set of behaviours that a class can sensibly implement, and I''d multiply inherit those interfaces to allow objects to be used in the various contexts that are applicable. I''d also probably use the Template Method design pattern to stabilise the interfaces, and possibly implement default behaviours within the interfaces, if I decided that was appropriate. Then I would make sure that all the objects in a collection share some behaviour in common, so that it makes sense for them to be grouped together and so that I wouldn''t have to downcast to get at that behaviour.

--

It is against the law to stare at the mayor of Paris.

Share this post


Link to post
Share on other sites
Inheritance can be used for more than just "is-a".

The whole notion of interfaces is not "is-a" but rather "does-this".

C++ has no interfaces as such, so has no way of explicitly saying "this close does these things", but it does have multiple inheritance, so one can write interface classes to establish such relationships. Using public inheritance to mean things other than "is-a" is common and quite an acceptable thing to do.

In any case, a common Object class would be unusual if it had no members at all. At the very least, it is useful to have a virtual clone() and create() function, and perhaps things like a toString() or a hashCode() function.

Share this post


Link to post
Share on other sites
quote:
Original post by DrPizza
Inheritance can be used for more than just "is-a".



Correct. C++ has private inheritance aswell. And protected inheritance, although nobody seems to have found a legitimate use for it.

quote:

The whole notion of interfaces is not "is-a" but rather "does-this".


This is semantic trickery! The OO notion of "IS-A" is modelled in C++ using public inheritance. Public inheritance always provides "IS-A" in terms of Liskov Sustitutability, whether that''s what you thought you modelled or not. If you want to take a semantic shift and rename "IS-A" to become "DOES-THIS" for certain contexts, then you could do that, but it is not common OO parlance.

quote:

C++ has no interfaces as such, so has no way of explicitly saying "this close does these things", but it does have multiple inheritance, so one can write interface classes to establish such relationships. Using public inheritance to mean things other than "is-a" is common and quite an acceptable thing to do.



Yes, it''s quite common, but I''m not so sure it''s acceptable. Can you provide an example of when it would be acceptable? A lot of the examples I see are a result of poor design.

quote:

In any case, a common Object class would be unusual if it had no members at all. At the very least, it is useful to have a virtual clone() and create() function, and perhaps things like a toString() or a hashCode() function.



You need to be careful of featurism. It is only useful to have those features if they are, well... useful. And it''s not necessarily a good idea to stick them in a common base class from which everything else inherits. I''d personally question whether everything in an app needs a clone() or create() function, or even if those functions make sense for all classes.


Share this post


Link to post
Share on other sites
There is a legitimate use for private/protected inheritance. Completely wrapping another class, for instance. Inheriting privately will disallow access to the original object from outside or to derived classes forcing them to use the wrapper instead.

Share this post


Link to post
Share on other sites
quote:
Original post by Kippesoep
There is a legitimate use for private/protected inheritance.


I''m fully aware of how to use private inheritance. It''s protected inheritance that I was talking about. It does give different levels of access to deriving classes, but I''ve never found that to be of any use.


Share this post


Link to post
Share on other sites
quote:
Original post by Kippesoep
There is a legitimate use for private/protected inheritance. Completely wrapping another class, for instance. Inheriting privately will disallow access to the original object from outside or to derived classes forcing them to use the wrapper instead.




and now for the great question, how on earth would that be better than having the wrapper contain the wrapped object?

that would mke it clearer what is actually intended.

Share this post


Link to post
Share on other sites
quote:
Original post by krez
Fragmo: i am not storing them as the base class... but for my inventory management stuff i have a base-class pointer to all those various derived-class objects, so they can all be listed. then, iff a particular element is selected i want to find out if it is a weapon or a potion, or whatever, and give the player options to use class-specific functions (weapon->equip, potion->drink, whatever).
sjelkjd: yah... but CWeapon/CPotion/CArmor "is-a" CThing... is it really improper to add additional functions/variables to a derived class which don''t exist in the base class? these new members (equip(), drink(), etc()) do not break their CThing-ness... but there is no reason to have an equip() function (even a virtual one) in six different classes when only one of them uses it. i thought this was the point of OOP...
i think i suck at explaining myself



Things to note:
1) yes you do have a desing problem.
2) down casting is the wrong way to solve it
3) SabreMan is be quite rude, trying to make us all admire his "leet" OO-designing skills...
4) why the hell did he mention "TemplateMethod" when the obvious solution to your problem is to use the "Visitor" pattern?
5) Would you understand if I tried to tell you how it works?

my tips for solving it:

1) Think about your design, you have a problem, one solution is the visitor pattern (google it).
2) just don''t
3) you werent that nice either, as he said he is giving of his free time... so is everybody else, smile and be happy. don''t yell or flame
4) still beats me? but he could have gotten angry and just missed it.
5) would you?

Share this post


Link to post
Share on other sites
quote:
Original post by DigitalDelusion
and now for the great question, how on earth would that be better than having the wrapper contain the wrapped object?

that would mke it clearer what is actually intended.


There is one reason for choosing protected inheritance rather than a wrapped object...when you need access to the protected members of the class you are extending.

EDIT: Actually, I meant to say private inheritance rather than protected inheritance, but I guess either could make sense

Edited by - LordKronos on February 26, 2002 12:55:53 PM

Share this post


Link to post
Share on other sites

  • Advertisement