Hi Everyone,
Please take a look at the following (simple and fun) program I wrote to
demonstrate my query:
////////////////////////////////////////////////////////////////////////////
/////////////
// 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
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Patchwork
|
11/16/2003 11:28:31 AM |
|
In article <bp6fru$hav$1@sparta.btinternet.com>,
"Patchwork" <IDontLikeSpam@IDontLike.Spam> wrote:
> Please take a look at the following (simple and fun) program I wrote to
> demonstrate my query:
>
[snip cute sample code]
>
> 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? :-) ).
In other words, you want double dispatch on the type of eater and the
type of food.
> 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.
This is my usual solution for asymmetric double dispatch (it won't work
for the symmetric case, where both arguments are from the same
hierarchy). It is actually a version of the GoF Visitor pattern.
Generalise to Feed(Eater&), where Eater is an abstract visitor base
class, and have CMonster& derive from Eater. Yes, each CFood-derived
class has to implement the boilerplate Feed(Eater&), but only once
total, not once for each type of Eater. IOW, each type of food has to
be generically eatable, but it doesn't need to know anything about
what's doing the eating. You could even bundle the boilerplate up in an
EAT_ME macro, reducing it to a single invocation of the macro in the
definition of each food class.
For other ways of doing this sort of thing, have a look at Item 31 in
More Effective C++ (Meyers) or the chapter on double dispatch in Modern
C++ Design (Alexandrescu).
> Would templates be an answer somewhere?
Not really. At first glance, it might seem that if you knew the type of
Eater at compile time, you could dispense with the abstract base class
and make Feed a template member function. But Feed has to be virtual,
and virtual functions can't be templates. (And I guess you are likely
to want runtime polymorphism on the type of Eater anyway.)
Best wishes,
Matthew Collett
--
Those who assert that the mathematical sciences have nothing to say
about the good or the beautiful are mistaken. -- Aristotle
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
0
|
|
|
|
Reply
|
Matthew
|
11/17/2003 12:27:56 AM
|
|
|
1 Replies
152 Views
(page loaded in 0.044 seconds)
|