2010-12-08 1 views
2

Предположим, что у меня есть следующая структура классов. Я хочу, чтобы иметь возможность определить, какой тип класса является элементом моего вектора Animal, чтобы я мог выполнять на нем подклассы. Пример, приведенный ниже, должен продемонстрировать:Вызов производного метода на элемент базового вектора (пример приведен)

#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; 
} 

Очевидно, что маркированный код не будет работать, но как мне сделать то, что я хочу сделать? Есть ли обходной путь или принцип дизайна, которым я должен следовать, но нет? Я посмотрел на dynamic_cast, но понимаю, что это неудобно. Итак, как мне это сделать правильно?

В Java, я хотел бы сделать это:

if (v.get(i).getClass() == Lion.class) { 
    ((Lion)v.get(i)).chowMeat(); 
} 
if (v.get(i).getClass() == Butterfly.class) { 
    ((Butterfly)v.get(i)).drinkNectar(); 
} 
+2

Почему бы не сделать их подклассом животного, если вы не собираетесь использовать виртуальные методы, чтобы у обоих животных был метод .eat(). Есть ли причина, по которой им нужны разные имена методов, чтобы сделать то же самое? Тогда они не должны наследовать от того же класса. – Falmarri 2010-12-08 00:43:40

+0

`dynamic_cast` - это« как это сделать правильно ». Это «неудобно», потому что, используя его здесь, вы неявно «не следуете принципу дизайна, который вам нужен». Явный эквивалент «по-разному» по той же причине, и это гораздо более очевидно, учитывая, как это написано. Кстати, используйте конструкции типа `foreach` для перебора контейнеров или, по крайней мере, используйте настоящие итераторы вместо индексов. См. Также http://stackoverflow.com/questions/4383250/why-should-i-use-foreach-instead-of-for-int-i0-ilength-i-in-loops. – 2010-12-08 01:55:21

ответ

0

Какова цель цикла? Это потреблять пищу? В этом случае добавьте virtual void consumeFood(int howMuch) в базовый класс и переопределите это в производных классах.

1

В идеале вы должны добавить виртуальную функцию в базовый класс, void eat(int quantity) и переопределить эту функцию в производных классах.

В этом случае может быть даже целесообразным сделать функцию не виртуальной и реализовать ее в базовом классе, поскольку оба производных класса выполняют ту же самую вещь.

Запрещая, что вы можете использовать dynamic_cast для проверки динамического типа объекта:

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

(на другую ноту, вы хотите быть очень осторожны при использовании голых указателей в C++, это очень сложно для написания правильного кода, где вы управляете ресурсами вручную. В вашем примере вы не освободили объекты, на которые указывают A и B, и таким образом просочились в них. Подумайте, используя автоматические указатели, например shared_ptr, для автоматического управления ресурсами.)

0

Почему бы не есть()?

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; 
} 
0

Если пример действительно репрезентативен, виртуальная функция быстрее решит вашу непосредственную проблему.

В любом случае простейший ответ, если ваши классы имеют виртуальные функции, заключается в использовании dynamic_cast для проверки того, является ли объект данного типа. Например:

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(); 
} 

Он построен в язык, и это только то, что для проверки, является ли на самом деле указывает указатель на базу объекта более производного типа.

Другой вариант - иметь какую-то виртуальную функцию GetType в базовом классе, которую вы переопределяете для каждого класса, чтобы возвращать что-то (целое число, объект и т. Д.), Который однозначно идентифицирует этот класс. Затем вы вызываете эту функцию во время выполнения и проверяете результат, чтобы узнать, на какой объект указывают.

dynamic_cast имеет то преимущество, что он встроен в язык и не требует никаких усилий для поддержки. Использование собственной функции имеет более предсказуемые характеристики производительности в широком спектре компиляторов и позволяет хранить данные, отличные от того, какой тип объекта действительно есть, - но вам нужно написать все это самостоятельно.

Смежные вопросы