A (Simple) Polymorphism Query

  • Follow


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)

Similiar Articles:







7/15/2012 11:40:24 PM


Reply: