Virtual functions and vectors

Started by
12 comments, last by Aardvajk 15 years, 4 months ago
I am trying to write a simple physics engine but i have a problem. I tried to reproduce the problem with this code. If i know how to solve THIS i know how to solve it in my simple physics engine:

#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;
   
class Animal
{
  public:
  virtual void WhatAmI(){cout<<"i am an animal!"<<endl;};      
};
            
class Bird : public Animal
{
      public:
      void WhatAmI(){cout<<"i am a bird!"<<endl;};   
}; 
                         
class World
{
private:
  std::vector<Animal> Animals;
public:
  void AddAnimal(Animal& a) { Animals.push_back(a); }
  Animal GetAnimal(int index){return Animals[index];}
};  

int main(int argc, char *argv[])
{
    Animal a;
    Bird b;
    World w;
    w.AddAnimal(a);
	w.AddAnimal(b);
	w.GetAnimal(0).WhatAmI();
	w.GetAnimal(1).WhatAmI();
    system("PAUSE");
    return EXIT_SUCCESS;
}

result: I am an animal! I am an animal! The problem is that w.GetAnimal(1) should be a bird, not just an animal. How can i get the vector to remember what kind of animal it is? Thanks in advance, sorry for my bad English :)
Advertisement
When dealing with values (as opposed to references), C++ always knows the exact type at compile time. You cannot have a polymorphic value.

What is happening is slicing: when "b" is passed to AddAnimal() only the Animal portion is copied into the vector, the rest is lost.

You need to store (smart) pointers in your vector. Or you can use a modified vector class (like boost::ptr_vector) that will take ownership of the pointers contained therein.
Ya. Its slicing.

STL containers use value semantics. That means when you insert an item into your vector, it actually ends up storing a copy of the object passed in. The problem is that your vector is parameterized to hold instances of your base class. This means when you insert an item, your base class copy constructor is invoked. Of course the copy constructor in your base class only knows how to copy its base class parts, and as a result it simply discards all the "data" specific to the passed in derived type.

To fix this you can use a pointer container. These are containers which can safely hold pointers to items on the heap and resolves you of the responsibility of having to manually free them. Check this out for more info.
Thanks for your replies!
Now the output is still the same and i can't figure out what i am doing wrong this time :p
#include <cstdlib>#include <iostream>#include <vector>using namespace std;#include "ptr_vector.h"using namespace stdx;   // for ptr_vector, ptr_vector_owner   class Animal{  public:  virtual void WhatAmI(){cout<<"i am an animal!"<<endl;};      };            class Bird : public Animal{      public:      void WhatAmI(){cout<<"i am a bird!"<<endl;};   };                          class World{private: ptr_vector<Animal> Animals;public:  void AddAnimal(Animal *a) { Animals.push_back(a); }  Animal GetAnimal(int index){return Animals[index];}};  int main(int argc, char *argv[]){    Animal* a = new Animal();    Bird* b = new Bird();    World w;    w.AddAnimal(a);	w.AddAnimal(b);	w.GetAnimal(0).WhatAmI();	w.GetAnimal(1).WhatAmI();    system("PAUSE");    return EXIT_SUCCESS;}
Quote: Animal GetAnimal(int index){return Animals[index];}


You take something out of container, and convert it into Animal.

If something at index happens to be a Bird, the above is effectively the same as:
Animal GetAnimal(){  Bird b;  return Animal(bird);}


You need to return a reference to Animal:
 Animal & GetAnimal(int index){return Animals[index];}
Animal GetAnimal(int index){return Animals[index];}


Now you're slicing when you return from this function. Nearly there [smile].

Animal &GetAnimal(int index){ return Animals[index]; }


HTH
I am really learning here :p
Thank you very much everyone!
Hi Martijnvdc,

The only change in ur program u have 2 make is change the objects to the pointer to the objects.... So the code becomes.....




#include <cstdlib>
#include <iostream>
#include <vector>
using namespace std;

class Animal
{
public:
virtual void WhatAmI(){cout<<"i am an animal!"<<endl;};
};

class Bird : public Animal
{
public:
virtual void WhatAmI(){cout<<"i am a bird!"<<endl;};
};

class World
{
private:
std::vector<Animal*> Animals;
public:
void AddAnimal(Animal* a) { Animals.push_back(a); }
Animal* GetAnimal(int index){return Animals[index];}
};

int main(int argc, char *argv[])
{
Animal* a= new Animal();
Bird* b= new Bird();
World w;
w.AddAnimal(a);
w.AddAnimal(b);
w.GetAnimal(0)->WhatAmI();
w.GetAnimal(1)->WhatAmI();

return EXIT_SUCCESS;
}



When u use pointer to objects its memory will be allocated at runtime(as opposed to the objects which get allocated at compile time).

Thanks and Regards,
Rohith.H.N
Thanks and Regards,Rohith.H.N
I see :p
Quote:Original post by Rohithzhere
When u use pointer to objects its memory will be allocated at runtime(as opposed to the objects which get allocated at compile time).


Actually, stack objects get allocated at runtime if they are not in the global scope. Global stack objects get constructed at runtime (right before main is called), but their data is allocated in the data seg. Hence allocated at compile time, constructed at runtime.

The point is - the problem you solved had nothing with when/how the objects were constructed. The crux of the problem was in providing std::vector a value instead of a reference.

In other words, this will yield the same results:

...std::vector&lt;Animal*&gt; Animals;...Animal a;Bird b;World w;w.AddAnimal(&a);w.AddAnimal(&b);

This topic is closed to new replies.

Advertisement