Виртуальные функции: перебор вектора «Базовый класс», заполненного объектами подкласса

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

(Действительно) Подробное описание: я пытаюсь смоделировать существо, имеющее набор поведений. Мой базовый класс является абстрактным только с двумя функциями (виртуальными), которые переопределены всеми подклассами:

class Behavior
{
public:
     Behavior();
    ~Behavior(void){}
 virtual void execute(){} 
 virtual BEHAVIOR_TYPE getType() {return m_Type;}


protected:
BEHAVIOR_TYPE m_Type;
};

Я создал несколько вариантов поведения детей, таких как перемещение, потребление, разведка и т. д.

class Move :
    public Behavior
{
public:
BEHAVIOR_TYPE getType() {return m_Type;}
    enum Direction {N, NE, E, SE, S, SW, W, NW};
Move(DOCO * d);
~Move(void);
void execute() ;
    Direction chooseDirection();
    void setDirection(Direction newDirection);
private:
    Direction m_Direction;
    DOCO *I;
BEHAVIOR_TYPE m_Type;

};

Я создал вектор, в который поместил экземпляры каждого подкласса Behavior, а также итератор для его обхода:

vector<Behavior> m_Behavior;
vector<Behavior>::iterator bIt;

Когда существо получает последовательность действий, я пытаюсь перебрать вектор, разыменовать итератор и вызвать функцию execute:

void World::justDoIt()
{
    for(dIt=myDOCO.begin(); dIt!=myDOCO.end(); ++dIt)
{
    vector<Behavior>::iterator myBehavior=(dIt)->getFirstBehavior();
    vector<Behavior>::iterator end=(dIt)->getLastBehavior();
    for(myBehavior; myBehavior!=end; ++myBehavior)

        (*myBehavior).execute();
}
}

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

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

Очевидно, я сделал ошибку и каким-то образом сказал программе, что хочу, чтобы они рассматривались как родители, а не как дети, но я не могу найти свою ошибку.

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

Любая помощь будет принята с благодарностью.


person Sisyphus    schedule 05.03.2011    source источник
comment
Должны быть десятки дубликатов для помещения объектов базового класса (а не указателей) в контейнер STL, хотя я, банкомат, не могу найти ни одного. Один из них, вероятно, должен стать часто задаваемыми вопросами.   -  person sbi    schedule 05.03.2011


Ответы (2)


Класс vector<Behavior> создает копии всего, что вы в нем храните, используя конструктор копирования Behavior::Behavior(const Behavior&);. Это разрушает полиморфизм. Вместо этого вам нужно использовать указатель или интеллектуальный указатель в контейнере:

vector<Behavior*> m_Behavior; // I will take care of new and delete
vector<shared_ptr<Behavior> > m_Behavior; // easier

Если у вас нет std::shared_ptr или std::tr1::shared_ptr в #include <memory> или подобном, возможно, вы можете использовать Boost.

person aschepler    schedule 05.03.2011
comment
если у вас есть std::shared_ptr, используйте здесь std::unique_ptr. - person Matthieu M.; 05.03.2011

Вы добавляете в вектор экземпляры классов. Это приводит к нарезке.

Что вам нужно сделать, так это добавить указатели на экземпляры поведения и изменить цикл, чтобы он выполнял

(*myBehavior)->execute();
person Jon    schedule 05.03.2011