2

Suppose that I have the following structure of classes. I want to be able to determine of what class type the element in my Animal vector is, so that I may perform subclass-specific methods on it. The example below should demonstrate:

#include <iostream>
#include <vector>

using namespace std;

class Animal {
    public:
    int foodcount;

    Animal() {
        foodcount = 0;
        cout << "An animal was created.\n";
    }
    virtual ~Animal() {
        cout << "An animal was destroyed.\n";
    }
};

class Lion : public Animal {
    public:
    Lion() {
        cout << "A lion was created.\n";
    }
    virtual ~Lion() {
        cout << "A lion was destroyed.\n";
    }
    void chowMeat(int howmuch) {
        foodcount += howmuch;
    }
};

class Butterfly : public Animal {
    public:
    Butterfly() {
        cout << "A butterfly was created.\n";
    }
    virtual ~Butterfly() {
       cout << "A butterfly was destroyed.\n";
    }
    void drinkNectar(int howmuch) {
       foodcount += howmuch;
    }
};

int main() {
    Animal* A = new Lion();
    Animal* B = new Butterfly();
    vector<Animal*> v;

    v.push_back(A);
    v.push_back(B);

    // a little later

    for (int i=0; i<v.size(); i++) {
        if (v[i] is a Lion) v[i]->chowMeat();  // will not work of course
        if (v[i] is a Butterfly) v[i]->drinkNectar();   // will not work of course
    }

    std::cin.get();
    return 0;
}

Obviously the marked code won't work, but how do I do what I want to do? Is there a workaround or a design principle that I should follow but am not? I've looked into dynamic_cast but understand that is unpretty. So how should I do it correctly?

In Java, I would do this:

if (v.get(i).getClass() == Lion.class) {
    ((Lion)v.get(i)).chowMeat();
}
if (v.get(i).getClass() == Butterfly.class) {
    ((Butterfly)v.get(i)).drinkNectar();
}
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Schreib
  • 21
  • 1
  • 2
    Why even make them a subclass of animal if you're not going to use virtual methods to make both animals have a method .eat(). Is there a reason that they need different method names to do the same thing? Then they shouldn't inherit from the same class. – Falmarri Dec 08 '10 at 00:43
  • `dynamic_cast` is "how to do it correctly". It's "unpretty" because by using it here, you're implicitly "not following a design principle that you should". The Java equivalent is "unpretty" for the same reason, and that much more obviously so considering how it's written. By the way, please use `foreach` type constructs to iterate over containers, or at least use real iterators instead of indexes. See also http://stackoverflow.com/questions/4383250/why-should-i-use-foreach-instead-of-for-int-i0-ilength-i-in-loops . – Karl Knechtel Dec 08 '10 at 01:55
  • Falmarri: +1 - the point of subtyping is to generalize. When a Lion chows meat and a Butterfly drinks nectar, they are both feeding themselves. The general description is that they are Animals that are **eating**. So we have a `virtual void eat()`, and implement it such that the Lion eat()s by chowing meat, and the Butterfly eat()s by drinking nectar. (I guess you could call it `feedSelf()` instead, but that's needlessly awkward. :) ) – Karl Knechtel Dec 08 '10 at 01:57

4 Answers4

1

Ideally you would add a virtual function to the base class, void eat(int quantity) and override that function in the derived classes.

In this case, it might even make sense to make the function non-virtual and implement it in the base class, since both derived classes do the exact same thing.

Barring that, you can use dynamic_cast to test the dynamic type of the object:

if (Lion* lion = dynamic_cast<Lion*>(v[i])) {
    lion->chowMeat(42); 
}
else if (Butterfly* butterfly = dynamic_cast<Butterfly*>(v[i])) {
    butterfly->drinkNectar(42);
}
// etc.

(On a different note, you'll want to be very careful using naked pointers in C++; it's very difficult to write correct code where you manage resources manually. In your example, you haven't freed the objects pointed to by A and B and have thus leaked them. Consider using smart pointers, like shared_ptr, to manage your resources automatically.)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 1
    Ah, I should have made the example more clear. I mean that the methods would be unique and do entirely different things. But thanks for the dynamic_cast answer - is there any other, prettier way to restructure it? – Schreib Dec 08 '10 at 01:10
  • @Schreib: I don't think this is ugly. It's sometimes necessary to use `dynamic_cast` and it's not absolutely wrong to use them. You could, of course, implement your own type identification system (e.g., using a base class `GetType()` function that returns a unique identifier that you can use to determine the type), but why roll your own type identification when the language has it built-in? – James McNellis Dec 08 '10 at 01:22
  • "sometimes necessary and not absolutely wrong" is absolutely not a defense to a charge of "ugly" :) – Karl Knechtel Dec 08 '10 at 01:53
  • @Karl: No, it's not. I don't think it's ugly, and I was attempting to preempt the anti-cast fanatics. – James McNellis Dec 08 '10 at 01:56
0

What is the purpose of the loop? Is it to consume food? In that case, add a virtual void consumeFood(int howMuch) to your base class, and override that in your derived classes.

EboMike
  • 76,846
  • 14
  • 164
  • 167
0

Why not eat()?

class Animal {
    public:
    int foodcount;

    Animal() {
        foodcount = 0;
        cout << "An animal was created.\n";
    }
    virtual ~Animal() {
        cout << "An animal was destroyed.\n";
    }
    virtual void eat(int howMuch) {
        foodcount += howmuch;
    }
};

class Lion : public Animal {
    public:
    virtual void eat(int howmuch) {
        Animal::eat(howmuch + 19);
    }
};

class Butterfly : public Animal {
    void eat(int howmuch) {
       Animal::eat(howmuch / 1000);
    }
};

class Tribble: public Animal
{
    void eat(int howmuch) {
       throw DontFeedTribles();
    }
};

int main() {
    std::auto_ptr<Animal> A = new Lion();
    std::auto_ptr<Animal> B = new Butterfly();
    vector<Animal*>  menagerie;

    menagerie.push_back(A.get());
    menagerie.push_back(B.get());

    BOOST_FOREACH(Animal* animal, menagerie)
    {
        animal->eat(10000);
    }

    std::cin.get();
    return 0;
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

If the example is genuinely representative, a virtual function would solve your immediate problem much more neatly.

In any event, the simplest answer, if your classes have virtual functions, is to use dynamic_cast to check whether an object is of a given type. For example:

for (int i=0; i<v.size(); i++) {
    if (Lion *lion = dynamic_cast<Lion *>(v[i]))
        lion->chowMeat();
    else if(Butterfly *butterfly = dynamic_cast<Butterfly *>(v[i]))
        butterfly->drinkNectar();
}

It is built in to the language, and is just the thing for checking whether a pointer to base actually points to an object of a more derived type.

The other option is to have some kind of virtual GetType function in your base class, which you override per class to return something (an integer, an object, whatever) that uniquely identifies that class. Then you call this function at runtime, and examine the result to find out what sort of object is being pointed at.

dynamic_cast has the advantage of being built in to the language, and requires no effort on your part to support. Using one's own function has more predictable performance characteristics across a wide range of compilers and allows one to store data other than simply what type the object really is -- but you do have to write it all yourself.