2

I am a beginner developing an ecosystem in C++. It works like this:

I have PLANTS(not shown here) and ANIMALS on my grid.

If something is an ANIMAL, it is either a CARNIVORE or an HERBIVORE.

I created this hierarchy:

class ANIMAL
{
private:
    int a_CUR_food_lvl; 
    const int a_MAX_food_lvl; 
    const int a_hunger_rate; 

public:

    ANIMAL(int curr, int max, int rate)
        : a_CUR_food_lvl(curr),
        a_MAX_food_lvl(max),
        a_hunger_rate(rate)
    {}

     virtual ~ANIMAL()
     {
        delete this;
     }

    virtual void Move() = 0;
    virtual void Eat() = 0;
    virtual void Evade() = 0;
    virtual void Reproduce() = 0;
    virtual void Die() = 0;
    virtual void Age() = 0;

    virtual int get_a_CUR_food_lvl() { return a_CUR_food_lvl; };
    virtual int get_a_MAX_food_lvl() { return a_MAX_food_lvl; };
    virtual int get_a_hunger_rate() { return a_hunger_rate; };
}; //end ANIMAL class


//#################################################################
class CARNIVORE : public ANIMAL
{
public:

    class WOLF : public ANIMAL
    {
        WOLF()
            : ANIMAL(150, 200, 2)
        {}
    };

    class CHEETAH : public ANIMAL
    {
        CHEETAH()
            : ANIMAL(75, 125, 5)
        {}
    };
}; //end CARNIVORE class


//#################################################################
class HERBIVORE : public ANIMAL
{
public:


    class RABBIT : public ANIMAL
    {
        RABBIT()
            : ANIMAL(150, 200, 2)
        {}
    };


    class SQUIRREL : public ANIMAL
    {
        SQUIRREL()
            : ANIMAL(150, 200, 2)
        {}
    };
}; //end HERBIVORE class

My Problem:

Later down the line, I want to use dynamic_cast to see if one animal is allowed to eat another animal. Specifically, Animal A is only allowed to eat animal B if A is a CARNIVORE and B an HERBIVORE.

Will nesting the classes like I did allow me to check the type of the object later in my programming? I wouldn't want to think this is correct now and have to change it down the line.

Or if there is a better way other than dynamic_cast (a boolean is_carnivore?) should I be using that instead? I'm aiming for the most readable and best OOP practice

Community
  • 1
  • 1
user3487243
  • 183
  • 1
  • 11

5 Answers5

4

Your nested classes do not at all represent what you want. Do not consider further this construct to address your problem. Instead:

  • WOLF and CHEETAH should inherit from CARNIVORE
  • SQUIRREL and RABBIT shoulr inherit from HERBIVORE

There are then several ways to see if animal *a can eat animal *b another. The simplest approach would be dynamic_cast as you've suggested:

ANIMAL *a, *b; 
...
if (dynamic_cast<CARNIVORE*>(a) && dynamic_cast<HERBIVORE*>(b)) {
     // *a can eat *b
}

But this would not be the best OOP practice. I don't know all your constraints and requirements, but if you want to be state of the art and be able to encode much more complex rules (like strength and health of protagonists, exceptions for some species, etc..) you may be interested in implementing some kind of double dispatch (such as discussed here

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • @user3487243 yes, this is what I meant for the class structure. – Christophe Apr 12 '16 at 21:38
  • It is a bit confusing if you start fixing the code that was the original answer, now it becomes impossible for new readers to follow the question. The nesting that you refer to in your question is nowhere to be seen anymore. – Chiel Apr 12 '16 at 21:39
  • 1
    Thanks @Chiel , I reverted to the original. I didn't know how else to ask Christophe if I was on the right track – user3487243 Apr 12 '16 at 21:44
3

If im correct, you could do something as simple as making each animal have its own level in the ecosystem. Something like this:

class Animal {
Animal(int curr, int max, int rate, int level)...

}

Then later on make it so the animals can only eat another animal with a lower level.

Something like:

if(animalA.level < animalB.level){
    ...
}else{
    ...
}

About the nests however im quite unsure if thats even legal. Never tried to do it like that, and i probably wont.

Håvard Nygård
  • 386
  • 3
  • 14
  • My carnivores won't each grass, but I can circumvent that much with another conditional. Thank you, this will work! – user3487243 Apr 12 '16 at 21:24
  • Note: The right approach, in my opinion, for doing the value checking in the second example, would be to make a return function for the value – Håvard Nygård Apr 12 '16 at 21:24
  • `Animal(int curr, int max, int rate, int level, bool isPlant)` could solve the plant/animal problem :) glad to help! – Håvard Nygård Apr 12 '16 at 21:25
1

You already have part of the answer, virtual functions are what you need. You would define a virtual function in your base class called isCarnivore() or isHerbivore() and then create the definition in the derived class to return true or false based on which type the object is.

Example:

class ANIMAL {
public:
    virtual bool isHerbivore() = 0; //overload this in your derived class to determine if it is a carnivore
    bool isCarnivore() {
        return !isHerbivore(); // if its an herbivore it cannot be a carnivore and vice versa
    }
};

class CARNIVORE : public ANIMAL {
    virtual bool isHerbivore() override {
        return false;
    }
};

class HERBIVORE : public ANIMAL {
    virtual bool isHerbivore() override {
        return true;
    }
};
Stephen
  • 1,498
  • 17
  • 26
1

Here you find a running example of my suggestion of two levels of inheritance (which I see has been posted in the meantime as an answer):

#include <iostream>

struct Animal
{
    virtual ~Animal() {};
};
struct Carnivore : public Animal {};
struct Herbivore : public Animal {};
struct Wolf      : public Carnivore {};
struct Cheetah   : public Carnivore {};
struct Squirrel  : public Herbivore {};
struct Rabbit    : public Herbivore {};

bool can_eat(Animal* predator, Animal* prey)
{
    return ( dynamic_cast<Carnivore*>(predator) && dynamic_cast<Herbivore*>(prey) );
}

int main()
{
    Animal* wolf   = new Wolf();
    Animal* rabbit = new Rabbit();

    std::cout << "Wolf eats rabbit = " << can_eat(wolf, rabbit) << std::endl;
    std::cout << "Rabbit eats wolf = " << can_eat(rabbit, wolf) << std::endl;

    return 0;
}
Chiel
  • 6,006
  • 2
  • 32
  • 57
1

You can simply add an attribute to the base class that denotes the food chain hierarchy. For example:

#include <iostream>
class Animal
{
    private:
        int a_CUR_food_lvl; 
        int food_chain_level;
        const int a_MAX_food_lvl; 
        const int a_hunger_rate; 

   public:
        Animal(int curr, int max, int rate, int food_chain)
        : a_CUR_food_lvl(curr), a_MAX_food_lvl(max),
        a_hunger_rate(rate), food_chain_level(food_chain) {}

        bool canEat(Animal& theAnimal) const
        { return food_chain_level > theAnimal.food_chain_level; } 
//...
}; 

class Carnivore : public Animal
{
   public:
        Carnivore(int curr, int max, int rate) : Animal(curr, max, rate, 2) {}
};

class Herbivore : public Animal
{
   public:
        Herbivore(int curr, int max, int rate) : Animal(curr, max, rate, 1) {}
};

class Insect : public Animal
{
   public:
        Insect(int curr, int max, int rate) : Animal(curr, max, rate, 0) {}
};

Carnivore Wolf(150, 200, 2);
Carnivore Cheetah(75,125,2);
Herbivore Squirrel(150,200,2);

using namespace std;

int main()
{
   cout << (Wolf.canEat(Squirrel)?"true":"false") << endl;
   cout << (Squirrel.canEat(Cheetah)?"true":"false") << endl;
}

Live Example

Note that I used a simple integer, but more than likely, a more sophisticated approach (maybe introduce size as another determining factor to which animal can eat the other animal). As my comment suggested, there are small carnivores that in no way could eat big herbivores.

PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45