A (Simple) Polymorphism Query

P

Patchwork

Hi Everyone,

Please take a look at the following (simple and fun) program:

////////////////////////////////////////////////////////////////////////////
/////////////
// Monster Munch, example program

#include <list>

class CFood
{
public:
CFood() {}
virtual ~CFood() {}
};

class CFoodCookie : public CFood
{
public:
CFoodCookie() {}
virtual ~CFoodCookie() {}
};

class CFoodHorse : public CFood
{
public:
CFoodHorse() {}
virtual ~CFoodHorse() {}
};

class CMonster
{
public:
CMonster() {}
~CMonster() {}

// The (greedy) monster may eat several things
void Eat(CFood* pSnack) {delete pSnack; printf("\nI ate summink, but I know
not what.");}
void Eat(CFoodCookie* pSnack) {delete pSnack; printf("\nYummy! A
cookie!");}
void Eat(CFoodHorse* pSnack) {delete pSnack; printf("\nI was so hungry I
ate a horse!");}
};

class CPantry
{
public:
CPantry() {}
~CPantry() {}

void AddFood(CFood* pFood)
{
// Add the food to our supplies
m_Stock.push_back(pFood);
}

void Feed(CMonster& Monster)
{
while (m_Stock.size())
{
// Get the food...
CFood* pFood = m_Stock.front();
m_Stock.pop_front();

// Feed the monster
Monster.Eat(pFood);
}
}

private:
std::list<CFood*> m_Stock;
};

int _tmain(int argc, _TCHAR* argv[])
{
CPantry ThePantry;

// Let's see what we have:
// Two cookies...
ThePantry.AddFood(new CFoodCookie());
ThePantry.AddFood(new CFoodCookie());
// Goodness knows what this is...
ThePantry.AddFood(new CFood());
// And this, apparently...
ThePantry.AddFood(new CFoodHorse());

// Feed the guest...
CMonster ScaryDude;
ThePantry.Feed(ScaryDude);

getchar();

return 0;
}
////////////////////////////////////////////////////////////////////

I may be overlooking a very simple trick indeed, but my question is this:
how do I have the monster know what he (or she, who knows?) is eating? There
are a couple of factors that I wish to preserve in the solution:
1. The monster must have the overloaded Eat members as I intend to implement
similar classes that will eat the same and different foods in different
manners (with manners, perhaps? :) ).
2. The pantry must be the source of food and must store food in a generic
manner.
3. I want users to implement new CFood derived classes as simply as
possible.

I could, for example, have a virtual Feed(CMonster&) method in all CFood
derived classes that executes Monster.Eat(this). However, this would impact
factor 3 in that all new CFood-derived classes would need to add this
method - if implemented in the CFood base class only, the monster would only
eat generic food.

What would be your suggestions, folks? Is there a simple, or non-simple
polymorphic step I am missing? Would templates be an answer somewhere?

Many, many thanks!
Lucy x
 
V

Victor Bazarov

Patchwork said:
Please take a look at the following (simple and fun) program:

////////////////////////////////////////////////////////////////////////////
/////////////
// Monster Munch, example program

#include <list>

class CFood
{
public:
CFood() {}
virtual ~CFood() {}
};

class CFoodCookie : public CFood
{
public:
CFoodCookie() {}
virtual ~CFoodCookie() {}
};

class CFoodHorse : public CFood
{
public:
CFoodHorse() {}
virtual ~CFoodHorse() {}
};

class CMonster
{
public:
CMonster() {}
~CMonster() {}

// The (greedy) monster may eat several things
void Eat(CFood* pSnack) {delete pSnack; printf("\nI ate summink, but I know
not what.");}
void Eat(CFoodCookie* pSnack) {delete pSnack; printf("\nYummy! A
cookie!");}
void Eat(CFoodHorse* pSnack) {delete pSnack; printf("\nI was so hungry I
ate a horse!");}
};

class CPantry
{
public:
CPantry() {}
~CPantry() {}

void AddFood(CFood* pFood)
{
// Add the food to our supplies
m_Stock.push_back(pFood);
}

void Feed(CMonster& Monster)
{
while (m_Stock.size())
{
// Get the food...
CFood* pFood = m_Stock.front();
m_Stock.pop_front();

// Feed the monster
Monster.Eat(pFood);
}
}

private:
std::list<CFood*> m_Stock;
};

int _tmain(int argc, _TCHAR* argv[])

This is not C++. In C++ the entry point should be called
'main' and have very particular parameters.
{
CPantry ThePantry;

// Let's see what we have:
// Two cookies...
ThePantry.AddFood(new CFoodCookie());
ThePantry.AddFood(new CFoodCookie());
// Goodness knows what this is...
ThePantry.AddFood(new CFood());
// And this, apparently...
ThePantry.AddFood(new CFoodHorse());

// Feed the guest...
CMonster ScaryDude;
ThePantry.Feed(ScaryDude);

getchar();

return 0;
}
////////////////////////////////////////////////////////////////////

I may be overlooking a very simple trick indeed, but my question is this:
how do I have the monster know what he (or she, who knows?) is eating?

Unless you somehow perform run-time type resolution (like dynamic_cast,
for example), and keep passing 'CFood*' to the overloaded member of
the CMonster class, CMonster::Eat(CFood*) _shall__always_ be chosen.

Now, there is a bigger problem here. Your CMonster will still _have_to_
know all the different foods there possibly can be in order to behave
correctly. That's no polymorphism.
There
are a couple of factors that I wish to preserve in the solution:
1. The monster must have the overloaded Eat members as I intend to implement
similar classes that will eat the same and different foods in different
manners (with manners, perhaps? :) ).

That means you will have to limit your monster to some _categories_ of
food, anyway. So, why don't you place that category table (or list, or
enumeration) into the CFood base class and let the monster behave
differently based on the category the food returns?
2. The pantry must be the source of food and must store food in a generic
manner.
3. I want users to implement new CFood derived classes as simply as
possible.

I could, for example, have a virtual Feed(CMonster&) method in all CFood
derived classes that executes Monster.Eat(this). However, this would impact
factor 3 in that all new CFood-derived classes would need to add this
method - if implemented in the CFood base class only, the monster would only
eat generic food.

In polymorphism, the only way to do it is to let CFood descendants
provide the distinguishing functionality. Otherwise, it's not the
polymorphism we know.
What would be your suggestions, folks? Is there a simple, or non-simple
polymorphic step I am missing? Would templates be an answer somewhere?

You're missing the whole point of polymorphism, I am afraid.

Victor
 
P

Patchwork

Hi Victor,

Thanks for your response...

----- Original Message -----
From: "Victor Bazarov" <[email protected]>
Newsgroups: comp.lang.c++
Sent: Sunday, November 16, 2003 4:27 AM
Subject: Re: A (Simple) Polymorphism Query

Please take a look at the following (simple and fun) program:
////////////////////////////////////////////////////////////////////////////
/////////////
// Monster Munch, example program

#include <list>

class CFood
{
public:
CFood() {}
virtual ~CFood() {}
};

class CFoodCookie : public CFood
{
public:
CFoodCookie() {}
virtual ~CFoodCookie() {}
};

class CFoodHorse : public CFood
{
public:
CFoodHorse() {}
virtual ~CFoodHorse() {}
};

class CMonster
{
public:
CMonster() {}
~CMonster() {}

// The (greedy) monster may eat several things
void Eat(CFood* pSnack) {delete pSnack; printf("\nI ate summink, but I know
not what.");}
void Eat(CFoodCookie* pSnack) {delete pSnack; printf("\nYummy! A
cookie!");}
void Eat(CFoodHorse* pSnack) {delete pSnack; printf("\nI was so hungry I
ate a horse!");}
};

class CPantry
{
public:
CPantry() {}
~CPantry() {}

void AddFood(CFood* pFood)
{
// Add the food to our supplies
m_Stock.push_back(pFood);
}

void Feed(CMonster& Monster)
{
while (m_Stock.size())
{
// Get the food...
CFood* pFood = m_Stock.front();
m_Stock.pop_front();

// Feed the monster
Monster.Eat(pFood);
}
}

private:
std::list<CFood*> m_Stock;
};

int _tmain(int argc, _TCHAR* argv[])

This is not C++. In C++ the entry point should be called
'main' and have very particular parameters.

Point taken, I overlooked this added by my compiler in a dash to create the
demonstration program.
Unless you somehow perform run-time type resolution (like dynamic_cast,
for example), and keep passing 'CFood*' to the overloaded member of
the CMonster class, CMonster::Eat(CFood*) _shall__always_ be chosen.

Again, thanks for taking the time to look over the code. I do see that this
is my problem but I was hoping there would be a simple and convenient way to
pass the desired class type without implementing this in CFood-derived
classes.
Now, there is a bigger problem here. Your CMonster will still _have_to_
know all the different foods there possibly can be in order to behave
correctly. That's no polymorphism.

Hmm. Again, I see your point. My subject heading is the erroneous factor
here. I do desire that the monster behaves this way with a pre-determined
list of foods specifically handled. Polymorphic behaviour, in its true
sense, is expected of the CFood classes within the CPantry class and
elsewhere. Consequently, it is only the CMonster class that I wish to
implement in this novel manner by reacting differently to different foods.
The 'handler' for the generic food type is there to allow the monster to eat
any type of food indiscriminately if required.
That means you will have to limit your monster to some _categories_ of
food, anyway. So, why don't you place that category table (or list, or
enumeration) into the CFood base class and let the monster behave
differently based on the category the food returns?

Indeed, the moster's CFood type handling isn't an overisight, as mentioned
above. The bigger picture is that we may have a CLifeform class with
subclasses that override handlers only for the foodtypes they are interested
in eating. Do you think categories via enumerated types, etc would be the
ideal solution? I was wanting to avoid implementing type checks/cases in the
CMonster class by having certain foods naturally handled in their
corresponding method overrides.
In polymorphism, the only way to do it is to let CFood descendants
provide the distinguishing functionality. Otherwise, it's not the
polymorphism we know.

Yes, again, I make no excuses for the mistitled e-mail but could you think
of any further tricks that may help me achieve what I hope for?
You're missing the whole point of polymorphism, I am afraid.

Well, let's say I suffered an aberrant thought when naming the message...:)

Perhaps I will rethink my overall design (surely your recommendation? :) )
but any further suggestions would be most appreciated. I don't really know
of any newsgroups that would be interested in discussing models, etc in C++
that would be interested in making some suggestions for another design. Any
help from anyone here in achieving what I desire would be _most_ appreciated
:)

To reiterate the requirements:
1) There is a class type CFood which will have more subclasses added in the
future. Each foodtype will have its own properties, etc making good use of
polymorphism where appropriate (honest).
2) There is a class CPantry which will store a dynamic quantity of CFood
based classes. The specific CFood-derived class type need not be known.
3) There is a class CLifeform that will have subclasses such as CMonster or
CHumanoid. Each CLifeform-based class may process or eat CFood-based objects
differently. A default 'handler' may be desirable just to mention that the
lifeform is not interest in the food type.
4) Other classes (not CLifeform-drived) may also 'handle' CFood objects.

If the above model is not appropriate...what could be changed? Or what
could be used in its place?

Perhaps it is more appropriate to post this as a new question...?

Thanks, Victor
Lucy x
 
V

Victor Bazarov

Patchwork said:
----- Original Message -----
From: "Victor Bazarov" <[email protected]>
Newsgroups: comp.lang.c++
Sent: Sunday, November 16, 2003 4:27 AM
Subject: Re: A (Simple) Polymorphism Query

Please take a look at the following (simple and fun) program:
[...]
Now, there is a bigger problem here. Your CMonster will still _have_to_
know all the different foods there possibly can be in order to behave
correctly. That's no polymorphism.

Hmm. Again, I see your point. My subject heading is the erroneous factor
here. I do desire that the monster behaves this way with a pre-determined
list of foods specifically handled. Polymorphic behaviour, in its true
sense, is expected of the CFood classes within the CPantry class and
elsewhere. Consequently, it is only the CMonster class that I wish to
implement in this novel manner by reacting differently to different foods.
The 'handler' for the generic food type is there to allow the monster to eat
any type of food indiscriminately if required.
That means you will have to limit your monster to some _categories_ of
food, anyway. So, why don't you place that category table (or list, or
enumeration) into the CFood base class and let the monster behave
differently based on the category the food returns?

Indeed, the moster's CFood type handling isn't an overisight, as mentioned
above. The bigger picture is that we may have a CLifeform class with
subclasses that override handlers only for the foodtypes they are interested
in eating. Do you think categories via enumerated types, etc would be the
ideal solution? I was wanting to avoid implementing type checks/cases in the
CMonster class by having certain foods naturally handled in their
corresponding method overrides.

Alright, given these new conditions, I don't think that enumerators
would be ideal. The _ideal_ would be to teach each life form to
recognise the food they can eat (based on some specific characteristics
of the food, like hallucinogen-ness) and store [a pointer to] a sample
in their "set of acceptable foods". The whole point of it is that you
(or your life forms) can never tell what foods they will encounter on
their path to oblivion, especially considering that foods and creatures
are not related types and can be created independently.

So, let's re-visit categories now. What you need to give your CFood
class is a way to express itself: soft, chunky, needs cooking, stinks,
liquid, fatty, etc., and also give your CLifeform a mechanism which
will calculate acceptability of a particular food based on its factors.
I imagine that it is possible that your monster would say "I don't know
what I just ate, but it tasted like horse".
[...]
Perhaps I will rethink my overall design (surely your recommendation? :) )
but any further suggestions would be most appreciated. I don't really know
of any newsgroups that would be interested in discussing models, etc in C++
that would be interested in making some suggestions for another design. Any
help from anyone here in achieving what I desire would be _most_ appreciated
:)

Two suggestions: James Coplien's "Advanced C++" (1991) and comp.object.
To reiterate the requirements:
1) There is a class type CFood which will have more subclasses added in the
future. Each foodtype will have its own properties, etc making good use of
polymorphism where appropriate (honest).

Sounds good. Among the properties you could add a name, which will
be set by a first creature ever trying the food (unless the food has
already been stored in a box with a known name). Other creatures
can then ask the food what its name is and store the name (not some
'sample') in the set of foods it likes.
2) There is a class CPantry which will store a dynamic quantity of CFood
based classes. The specific CFood-derived class type need not be known.

Sure. Seems OK. Unless the food needs to be refridgirated to be
OK (or does your pantry have a fridge in it?)
3) There is a class CLifeform that will have subclasses such as CMonster or
CHumanoid. Each CLifeform-based class may process or eat CFood-based objects
differently. A default 'handler' may be desirable just to mention that the
lifeform is not interest in the food type.

Well, see above. I think that more sophisticated mechanism for the
life forms to distinguish between foods is needed.
4) Other classes (not CLifeform-drived) may also 'handle' CFood objects.

If the above model is not appropriate...what could be changed? Or what
could be used in its place?

It's appropriate. It's just too generic. You might want to begin
stating the details, for example, how would "other classes" "handle"
food? Spoil it? Heat it? A food's properties should probably
change based on that. Have you thought of what properties and how
are going to change?
Perhaps it is more appropriate to post this as a new question...?

Nah, just keep digging, I am fairly certain you're on the right track.

Good luck, and keep asking those questions!

Victor
 
J

jeffc

Patchwork said:
I don't really know
of any newsgroups that would be interested in discussing models, etc in C++
that would be interested in making some suggestions for another design.

comp.object
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,609
Members
45,253
Latest member
BlytheFant

Latest Threads

Top