Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Vector iterate over abstract class

Background info: Me and a couple of friends are building this platform game in C++ with the help och sfml and box2d for a school assignment. One of the requirements is that we follow the "MVC pattern".

We have created classes like Bullet and Character for the model. And BulletView and CharacterView (both inherits sf::Drawable which is an abstract class) for the view.

Instead of duplicating code for drawing and have two methods drawBullets and drawCharacter like this

void WorldView::drawBullets()
{
    std::vector<BulletView*>::iterator it;
    for ( it = bullets.begin() ; it < bullets.end(); it++ )
        window->draw(**it);
}

void WorldView::drawCharacters()
{
    std::vector<CharacterView*>::iterator it;
    for ( it = characters.begin() ; it < characters.end(); it++ )
        window->draw(*it);
}

I would like a more generic method using polymorhism which would look something like this:

void WorldView::drawVector(const std::vector<sf::Drawable*>& vector)
{
    std::vector<sf::Drawable*>::iterator it;
    for ( it = vector.begin() ; it < vector.end(); it++ )
        window->draw(**it);
}

The bulletView vector is declared like this:

std::vector<BulletView*> bullets;

Can't get this to work though. And I'm kind of new to C++ so please have mercy! I've tried searching but have not found very specific answers.

Errors I get while compiling.

Error 8 error C2679: binary '=' : no operator found which takes a right-hand >operand of type 'std::_Vector_const_iterator<_Myvec>' (or there is no acceptable >conversion) c:\users\niklas\multiplaya\sfml test\sfml test\view\worldview.cpp 409 >1 SFML_Test

Error 7 error C2664: 'mp::WorldView::drawVector' : cannot convert parameter 1 from >'std::vector<_Ty>' to 'const std::vector<_Ty> &' c:\users\niklas\multiplaya\sfml >test\sfml test\view\worldview.cpp 402 1 SFML_Test

like image 819
Niklas Andréasson Avatar asked May 04 '12 09:05

Niklas Andréasson


4 Answers

The problem is that the polimorphism doesn't work at the level of the vectors, but at the levevel of the pointers they contain. So std::vector<Derived*> can never be passed as an std::vector<Base*>, since they are completely different types. However, you can fill an std::vector<Base*> with pointers to Derived*.

So, you should declare your bullet vector as

std::vector<Drawable*> bullets;

and fill it with BulletView*, for example:

bullets.push_back( new BulletView( some args) );

drawVector(bullets);

Note: In the code above I am sidestepping the issue of dynamically allocated object ownership. Obviously some mechanism must be in place for deleting the BulletViews at the right moment.

like image 138
juanchopanza Avatar answered Nov 05 '22 08:11

juanchopanza


Instead of using polymorphic vectors, use template function:

template <typename T>
void WorldView::drawVector(const std::vector<T*>& vector)
{
    typename std::vector<T*>::iterator it;
    for ( it = vector.begin() ; it < vector.end(); it++ )
        window->draw(**it);
}

By the way - vectors of raw pointers look suspicious to me. Who owns them (who creates them and who is responsible to delete them)? Consider using vectors of values or boost::ptr_vector

like image 41
Tadeusz Kopec for Ukraine Avatar answered Nov 05 '22 08:11

Tadeusz Kopec for Ukraine


There errors you're getting are produced from the assignment it = vector.begin(); in your for loop. The problem is you're trying to assign the iterator(it) to const iterator(vector.begin()). You can't iterate over a data structure with a const iterator, because its value changes on each iteration. Also when traversing a data structure using iterators you should use the operator != instead of operator <.

If you remove the const from const std::vector& vector things should work just fine.

Hope this helps!

like image 2
Tomislav Dyulgerov Avatar answered Nov 05 '22 06:11

Tomislav Dyulgerov


You are imitating the Composite Design-Pattern.

However your error is: you should write replacing < by != because it is an iterator not an int

void WorldView::drawBullets()
{
    std::vector<BulletView*>::iterator it;
    for ( it = bullets.begin() ; it != bullets.end(); it++ )
        window->draw(**it);
}

void WorldView::drawCharacters()
{
    std::vector<CharacterView*>::iterator it;
    for ( it = characters.begin() ; it != characters.end(); it++ )
        window->draw(*it);
}
I would like a more generic method using polymorhism which would look something like this:

void WorldView::drawVector(const std::vector<sf::Drawable*>& vector)
{
    std::vector<sf::Drawable*>::iterator it;
    for ( it = vector.begin() ; it != vector.end(); it++ )
        window->draw(**it);
}
like image 1
Stephane Rolland Avatar answered Nov 05 '22 08:11

Stephane Rolland