Sign in to follow this  

Passing with Inhertitance classes.

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

Ok to start with I'm a bit sick, so if this is a "duh" question sorry. If I write a program that uses a "mesasge" class and the message class is set up something like this Class message { selection *choice; int number string stuff //other stuff } And these are other classes Class selection { //almost nothing. (this is a wrapper class) } Class player: private selection { //tons of data. } Now first assume that player is only one of 3 or four "selections" you can use. Then I should be able to pass a player pointer that's cast as a Selection pointer to message to "write" the message. then when I read the message can I then recast the selection pointer BACK to a Player pointer. If there's a correct way to do this? Now obviously I need to make sure to recast it into the CORRECT pointers. and I definatly will, but I want to make sure that this is possible and kosher.

Share this post


Link to post
Share on other sites
The key here is that it's just considered good programming practice to only derive a class from another class type when they have something in common (at least in my head.)

There may be more plausible solutions to your problem depending on what exactly you mean for it to do. For example, will every class derived from a Selection class contain a mechanism to retrieve a message of type MessageType?

To clarify:

class Message
{
public:
Selection *mChoice;
// ...
};

class Selection
{
private:
// all classes derived from Selection will be using this, won't they?
// coding it in the base class will save some time and effort:
MessageType mMyMessage;

public:
Selection (void);
virtual ~Selection (void);

// method to retrieve a message, since every derived class will use this
// exact block of code:
const MessageType getMessage (void) const
{
return mMyMessage;
}

// define some pure virtual functions that all derived classes will provide:
virtual void updateMessage (void) = 0;
// ...
};

class Player : public Selection
{
private:
// hey, I don't need any private data! I inherit stuff from a base class.

public:
Player (void);
virtual ~Player (void);

// function to update the mMyMessage variable specifically from this
// class instance:
void updateMessage (void);

private:
// define methods I use internally - e.g., a function I call from
// updateMessage() to update a message or something.
};


Using something like that, you ensure that no casting is necessary. A Player class will always be compatible with a Selection class, because all necessary methods (read: methods defined in the Selection class - especially pure virtual ones like updateMessage()) are defined in the Selection base class.

I'm not quite certain that I made a really adequate example, but I hope you get the gist of what I'm trying to say. But again, it really does depend on what you're trying to do with classes derived from the Selection class.

Regards,
-svx

Share this post


Link to post
Share on other sites
Again I might not be seeing this because I'm a little off today, but I think you're going in a different directions. I'll give a larger example.

I am using a multi threaded system, which has a logic thread, and a IO thread (and others)

Now the game thread, needs to ask the IO thread "which card should I pick" "Which player do I cast this spell on" "which magic" and multiple other systems. To facilitate this I've made a "message" class which passes an integer, a string, and a pointer to a class called "selection" back and forth between the participants.

Class selection
{}
Class card:public selection
{}
Class player :public selection
{}
Class monster:public selection
{}

So the message would hold a pointer to selection. Selection wouldn't even be aware of the message.


Basically I want to find out if this snippit is ok.

Card *currentcard= new Card ()
Selection *tempselection= (*Selection) currentcard
Card *nextcard= (*Card) tempselection

or would this crash somewhere in these three lines?

Share this post


Link to post
Share on other sites
Quote:

Basically I want to find out if this snippit is ok.

Card *currentcard= new Card ()
Selection *tempselection= (*Selection) currentcard
Card *nextcard= (*Card) tempselection

or would this crash somewhere in these three lines?


Ah, I see. That code should work flawlessly. If ever in doubt about casts that compile cleanly, you might consider using static_cast<Card *> (tempselection), etc. to test whether such a cast is really compatible at compile-time. C-style casts tend to be a lot more lenient, but don't provide as much error checking until run-time (read: you'll either know it's a bad cast when it crashes, or you'll spend a few hours trying to determine why it is that it did crash!)

static_cast's only succeed if there's an implicit conversion possible between a type and its given argument.

Regards,
-svx

[Edited by - svx on February 8, 2005 3:34:19 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by kinglink
Again I might not be seeing this because I'm a little off today, but I think you're going in a different directions. I'll give a larger example.

I am using a multi threaded system, which has a logic thread, and a IO thread (and others)

Now the game thread, needs to ask the IO thread "which card should I pick" "Which player do I cast this spell on" "which magic" and multiple other systems. To facilitate this I've made a "message" class which passes an integer, a string, and a pointer to a class called "selection" back and forth between the participants.

Class selection
{}
Class card:public selection
{}
Class player :public selection
{}
Class monster:public selection
{}

So the message would hold a pointer to selection. Selection wouldn't even be aware of the message.


Basically I want to find out if this snippit is ok.

Card *currentcard= new Card ()
Selection *tempselection= (*Selection) currentcard
Card *nextcard= (*Card) tempselection

or would this crash somewhere in these three lines?



Err yeah that should be fine. But be warey of downcasting from a common ancestor. Avoiding reflection, if you have some kind of enumeration property that indicates type in the Selection base class you can then switch on that and make the correct downcast. If you replaced the last line with

Card *nextcard= (*Monster) tempselection

it would fall over.

Share this post


Link to post
Share on other sites
You can always up-cast with no problem. You don't even need to assign to a temporary:
Selection *currentcard = new Card(); // perfectly OK so far


Now, this object knows how to do things in "the Card way", but it has "forgotten" that it is a Card - at compile-time, only the base Selection behaviour is available without casting:
currentcard->doSelectionThing(); // ok
// When this actually runs, if Cards provide an override (or implementation of
// pure virtual) for doSelectionThing(), then it will be used instead of the
// base Selection::doSelectionThing().
currentcard->doCardThing(); // error: compiler doesn't know it's a card any more


Now, there are a couple of options at this point, depending on what you "know".

If you can prove statically that your Selection is a Card (e.g. if you're doing this right away, and probably should have used a Card *-typed variable in the first place), then a static cast is fine:

static_cast<Card*>(currentcard)->doCardThing(); // compiler shuts up


However, if you're wrong, it will (probably) blow up at run-time.

If you don't know, then you will have to insert a bit of logic to check at run-time if things are what you think they are:
Card* tmp = dynamic_cast<Card*>(currentcard);
if (tmp) tmp->doCardThing();


The dynamic_cast will return a pointer to the same thing - except now known to be of the derived type - if it actually is a compatible type; otherwise, it returns a null pointer. That protects you from having things blow up, but you (a) still need to handle the "you're wrong" case somehow, and (b) have added some overhead (you will also need to enable RTTI for your project).

So generally, the rule is to avoid this kind of thing as much as possible - the whole idea with inheritance and polymorphism is to only use the methods you know are available, and rely on them to magically do what they need to (depending on the exact type of what you have - that's the 'polymorphism' part). If you're relying on functionality being there that you're not sure will be there, something smells. Of course, there are various ways around the problem, and the best solution depends on context - static_cast and dynamic_cast are among the tools available; you could also consider promoting the function to the base class and providing "dummy" implementations for classes that can't really do the specified thing. Don't be afraid to Think(TM) about this one - although the best thing is to look for a way not to get into the situation in the first place.


class Food: public FitsInMouth {
public: void getNutrition() {}
};

class Poison: public FitsInMouth {};

void consider(FitsInMouth& somethingIFoundUnderTheSink) {
// static_cast<Food>(somethingIFoundUnderTheSink).getNutrition();
cout << "Remember boys and girls:" << endl
<< "o/~ If you don't know just what it is..." << endl
<< "... don't put... it... in... your... mouth...! o/~" << endl;
}


Share this post


Link to post
Share on other sites
I'll consider that enumeration, I'm sure there'll be cards that will wipe out either a Player or a Monster/space/ tons of other stuff so that'll be worst and instead of using a 100 different messages enumeration will make it quite a bit easier. Thanks.

and thanks everyone else.

Share this post


Link to post
Share on other sites

This topic is 4690 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this