Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual Functions: Iterating over a vector<Base Class> that is populated with subclass objects

Short Description:
I am iterating over a vector calling a virtual function on every object in the vector in order to execute a sequence of actions. The vector is of the base class as is the iterator. All the objects are children. When the virtual function is called it executes the base class's function.

(Really) Long Description: I am attempting to model a creature that has a set of behaviors. My base class is abstract with only two functions (virtual) which all the subclasses have overridden:

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


protected:
BEHAVIOR_TYPE m_Type;
};

I have created a number of children behaviors, such as move, consume, scout, etc.

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;

};

I created a vector onto which I pushed instances of each Behavior subclass as well as an iterator to traverse it:

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

When the creature gets an action sequence, I try to iterate over the vector, dereference the iterator, and call the execute function:

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

The problem is that it executes the parent's function rather than the child's function.

In my understanding of late binding it should automatically call the appropriate function based on the type of object that was calling it rather than the type of pointer with which it was called, and in my code I anticipated it would be pointing to the child object.

Obviously I have made an error and somehow told the program that I want these to be treated as parents instead of children, but I cannot find my error.

A second symptom is that it will not let me make the parents function pure virtual, because it says it cannot instantiate an abstract class. I am not instantiating it explicitly anywhere in my code, but there must be somewhere I am doing it implicitly. I cannot find where, however. Certainly creating a vector to hold objects of the parent class does not require instantiating the parent, and that is the only time I reference the parent class directly.

Any help would be greatly appreciated.

like image 856
Sisyphus Avatar asked Mar 05 '11 00:03

Sisyphus


2 Answers

You are adding instances of classes to the vector. This results in slicing.

What you need to do is add pointers to instances of behaviors, and modify the loop to do

(*myBehavior)->execute();
like image 37
Jon Avatar answered Oct 20 '22 01:10

Jon


The class vector<Behavior> makes copies of whatever you store inside it, using the copy constructor Behavior::Behavior(const Behavior&);. This destroys the polymorphism. You need to use a pointer or smart pointer in the container instead:

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

If you don't have std::shared_ptr or std::tr1::shared_ptr in #include <memory> or similar, maybe you can use Boost's.

like image 110
aschepler Avatar answered Oct 20 '22 01:10

aschepler