vector

hey guys..so everytime my vector has a size of 1 and I erase that...there's problem...

btw here's my code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <cmath>
#include <iostream>

const float pi = 4 * std::atan(1);

class User : public sf::Drawable, public sf::Transformable
{
private :
    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
            states.transform *= getTransform();
            states.texture = NULL;
            target.draw(m_Vertices, states);
        }
    sf::VertexArray m_Vertices;
    float speed;
    float rotation;
    bool shoot = false;
    sf::Time CoolDown;
public :
    User() :
        speed(0.5),
        rotation(0.5)
    {
        m_Vertices.resize(3);
        m_Vertices.setPrimitiveType(sf::Triangles);
        //shapes
        m_Vertices[0].position = sf::Vector2f(0, 0);
        m_Vertices[1].position = sf::Vector2f(37, 18);
        m_Vertices[2].position = sf::Vector2f(0, 37);
        //color
        m_Vertices[1].color = sf::Color(0, 0, 120, 255);
        this->setOrigin(18, 18);
        this->move(18, 18);

    }
    void Action(const sf::RenderWindow &window)
    {
        float radians = (this->getRotation() * pi) / 180;
        float TempSpeed = speed;
        sf::Vector2f OldPos = this->getPosition();
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift))
        {
            TempSpeed += 0.5;
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
        {
           sf::Vector2f direction;
           direction.x = TempSpeed * std::cos(radians);
           direction.y = TempSpeed * std::sin(radians);
           this->move(direction);
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
        {
            sf::Vector2f direction;
            direction.x = -(TempSpeed * std::cos(radians));
            direction.y = -(TempSpeed * std::sin(radians));
            this->move(direction);
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
        {
            this->rotate( -(rotation) );
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
        {
            this->rotate(rotation);
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space))
        {
            if (this->CoolDown > sf::milliseconds(0)) return;
            this->CoolDown = sf::milliseconds(200);
            this->setShoot(true);
        }
        check(window, OldPos);
    }
    void check(const sf::RenderWindow& window, sf::Vector2f pos)
    {
        if (this->getPosition().x < 0) this->setPosition(pos);
        else if (this->getPosition().x > window.getSize().x) this->setPosition(pos);
        if (this->getPosition().y < 0) this->setPosition(pos);
        else if (this->getPosition().y > window.getSize().y) this->setPosition(pos);
    }
    bool& getShoot()
    {
        return this->shoot;
    }
    void setShoot(bool val)
    {
        this->shoot = val;
    }
    void updateCooldown(sf::Time& elapsed)
    {
        this->CoolDown -= elapsed;
    }
};

class Bullet
{
private :
    sf::RectangleShape Rect;
    float speed;
public :
    Bullet(User& user) :
        speed(5)
    {
        this->Rect.setSize(sf::Vector2f(10, 5));
        this->Rect.setOrigin(Rect.getSize().x / 2, Rect.getSize().y / 2);
        this->Rect.setPosition(user.getPosition());
        this->Rect.setRotation(user.getRotation());
        user.setShoot(false);
    }
    sf::Shape& getShape()
    {
        return Rect;
    }
    void update()
    {
        float radians = (Rect.getRotation() * pi) / 180;
        sf::Vector2f direction;
        direction.x = speed * std::cos(radians);
        direction.y = speed * std::sin(radians);\
        this->Rect.move(direction);
    }
    bool check(const sf::RenderWindow& window)
    {
        if (this->Rect.getPosition().x < 0) return true;
        else if (this->Rect.getPosition().x > window.getSize().x) return true;
        if (this->Rect.getPosition().y < 0) return true;
        else if (this->Rect.getPosition().y > window.getSize().y) return true;
        return false;
    }
};

int main()
{
    sf::RenderWindow window(sf::VideoMode(900, 600), "lol window");
    User user;
    std::vector <Bullet> bullet;
    sf::Clock clock;
    sf::Time elapsed;
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) window.close();
        }
        if (bullet.size() == 0);
        else
        {
            for (auto i = bullet.begin(); i != bullet.end(); ++i)
            {
                i->update();
                if (i->check(window))
                {
                    if (bullet.size() == 1) bullet.clear();
                    else bullet.erase(i);
                }
            }
        }
        user.Action(window);
        if (user.getShoot())
        {
            bullet.push_back(Bullet(user));
        }
        elapsed = clock.restart();
        user.updateCooldown(elapsed);
        window.clear();
        window.draw(user);
        for (auto i : bullet)
        {
            window.draw(i.getShape());
        }
        window.display();
    }
}


welp...it appeared fixed for some mystical reason that I dont know by just changing this code

150
151
152
153
154
155
156
157
158
159
160
161
162
if (bullet.size() == 0);
        else
        {
            for (auto i = bullet.begin(); i != bullet.end(); ++i)
            {
                i->update();
                if (i->check(window))
                {
                    if (bullet.size() == 1) bullet.clear();
                    else bullet.erase(i);
                }
            }
        }


into this

150
151
152
153
154
155
156
157
158
159
160
161
162
        if (bullet.size() == 0);
        else
        {
            for (int i = 0; i < bullet.size(); ++i)
            {
                bullet.at(i).update();
                if (bullet.at(i).check(window))
                {
                    bullet.erase(bullet.begin() + i);
                    std::cout << bullet.size();
                }
            }
        }
Last edited on
there's problem...

Very descriptive, I have to say.
welp, I thought I described the problem already ?
also, it just crashed, no exception occured tbh
try removing the SFML header files, re-create the problem in a native C++ program and post that instead. that way more folks will be able to replicate your problem and suggest solutions
I see..will do that right away..


(I tested the vector in a much more simplistic way... )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <vector>
#include <iostream>
#include <string>
#include <conio.h>

class Test
{
private :
    std::string output;
public :
    Test(std::string str)
    {
        output = str;
    }
    void print()
    {
        std::cout << output;
    }
};

int main()
{
    std::vector<Test> test;
    test.push_back(Test("lol"));
    test.erase(test.begin());
}


no problem in the code above though

weird...considering my problem is the fact that when the vector is 1 and I tried to clear it...my program crashes


and the fixed for my program right now is...to not erase the vector if the size is only 1
Last edited on
the problem might be here, line 150: if (bullet.size() == 0); the check for bullet.size() is not complete
thank you for your guidance...but as you have seen somehow I have fixed it...although I do not understand what I have done...
yes, i saw the green tick after that - i would like to say 'good job' but
I do not understand what I have done...
leaves me a bit queasy. anyways, if you're happy who am i to adjudicate?
well I was hoping you could explain to me what I have done..considering I edited my original post and put what I changed
I edited my original post and put what I changed

that's the problem you see, when you edit the post without telling anyone that you've done so (a) only you know that the post has been edited and (b) there's no way to tell what changes have been made to the OP
I see..

well, I dont understand why my program work when I change this piece of code from
150
151
152
153
154
155
156
157
158
159
160
161
162
if (bullet.size() == 0);
        else
        {
            for (auto i = bullet.begin(); i != bullet.end(); ++i)
            {
                i->update();
                if (i->check(window))
                {
                    if (bullet.size() == 1) bullet.clear();
                    else bullet.erase(i);
                }
            }
        }


to this

150
151
152
153
154
155
156
157
158
159
160
161
162
        if (bullet.size() == 0);
        else
        {
            for (int i = 0; i < bullet.size(); ++i)
            {
                bullet.at(i).update();
                if (bullet.at(i).check(window))
                {
                    bullet.erase(bullet.begin() + i);
                    std::cout << bullet.size();
                }
            }
        }
Last edited on
Well, I wrote a nice explanation, but then accidentally closed my browser.

Apologies, then:
Neither bit of code is correct. The problem is iterator invalidation:
http://stackoverflow.com/a/6442829

Specifically, std::vector::erase invalidates any iterators or references to elements of the container at or after the removed element. This means that erasing i and then continuing as if nothing happened is incorrect in either case, and you're just getting lucky.

One solution is to shift the elements you want to remove to the beginning of the sequence and then drop them. This is the erase-remove idiom:
https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
Last edited on

accidentally closed my browser.

welp guess I wont get to know what the nice explanation is then...it's fine though
but wait, why did I get lucky ??????
oh and... I tried to use it....but it requires me to implement == operator for my class....
so uhh...any way to work around this without implementing == operator ??
but wait, why did I get lucky ??????

You got lucky because in either case you erase the ith element, which invalidates i. Because the erase operation usually shifts elements, i's referent silently changes.

Basically, the behavior is undefined after that point, but it's not hard to imagine your code skipping the elements which directly follow the point of erase, accessing out of bounds, and so on.

If you can't simply implement operator==, for vectors you can check and erase the element at index i + 1. But this is a brittle (breaks silently when you change the container type) and awfully slow (O(n^2)) solution. It's not clear why the structs need to be equality-comparable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# include <algorithm>
# include <iostream>
# include <vector>

struct A {
    A(int const val): i{val} {}
    bool check() const noexcept { return i % 2; } 
    void update() { std::cout << "updated " << ++i << '\n'; } 
    int i = 0;    
};

int main() { 
    std::vector<A> v0{0, 1, 2, 3, 4, 5};

    // erase-remove: update every struct, removing those for which elt.check() 
    // returns true 
    v0.erase(std::remove_if(v0.begin(), v0.end(), 
             [](auto elt) { elt.update(); return elt.check(); }), v0.end());
}


http://coliru.stacked-crooked.com/a/5b89e50f81ab41f6
Last edited on
thank you for your guidance
Topic archived. No new replies allowed.