iterate through vector with for loop in c++ - c++

I am reading a tutorial about joystick input handling with SDL and I am struggling with a part of the code.
In .h file I got:
std::vector<std::pair<Vector2D*, Vector2D*> > m_joystickValues;
and in .cpp file I got:
m_joystickValues.push_back(std::make_pair(new Vector2D(0,0),new Vector2D(0,0)));
In this case I have one joystick, if there is many joysticks there will more push_backs, I want to access the adresses in "m_joystickValues" so I can delete them in a clean function.
Anyone has an idea how I can do this with a for loop. Thanks

For example you can use the range-based for statement
for ( auto &p : m_joystickValues )
{
delete p.first;
delete p.second;
}
The same can be done using the ordinary for statement
for ( size_t i = 0; i < m_joystickValues.size(); i++ )
{
delete m_joystickValues[i].first;
delete m_joystickValues[i].second;
}
Or you can use standard algorithm std::for_each with an appropriate lambda-expression. It is similar to using a for statement with iterators.

for(int i = 0; i < m_joystickValues.size(); i++)
{
m_joystickValues[i] //do something with this
}
Is this what you are looking for? Also, you could use the at function, since it is more safe.

for(auto& i : m_joystickValues)
{
delete i.second;
delete i.first; // or do whatever
}
At end of the loop you can erase the entire vector
m_joystickValues.erase(m_joystickValues.begin(), m_joystickValues.end());

Related

How to remove elements from a vector that uses the vector size in the for loop

I have a game where I shoot bullets at an object and I delete the object that gets hit by the bullet and bullet that are off screen as well.
For example:
std::vector<object> object_list;
for(size_t i = 0; i < object_list.size(); i++)
{
if(object_list[i].hit())
{
object_list.erase(object_list.begin() + i);
}
else
object_list[i].draw();
}
The problem with this is that when i remove an object, the size of the vector decreases so when it check the conditions, it fails and i get an error such as " vector subscript out of range." I could just choose not to render the asteroid by rendering those that haven't been hit, but the problem with that is that the no. of objects increases when hit(splits up) so eventually the program is going to get slower. I've used a similar concept for the off screen bullets but I can't find a way around it. I'm looking for a solution to this or better way of removing elements.
Both object and bullet are classes.
You should split for loop in 2 parts:
remove all "hit" elements:
object_list.erase(std::remove_if(object_list.begin(),
object_list.end(), [](auto&& item) { return item.hit(); }),
object_list.end());
draw remaining:
std::for_each(object_list.begin(), object_list.end(), [](auto&& item) { item.draw(); });
It's safer and more readable.
Same idea as the other answers but this code is a little easier with iterators
for (auto i = object_list.begin(); i != object_list.end(); )
{
if (i->hit())
{
i = object_list.erase(i);
}
else
{
i->draw();
++i;
}
}
vector::erase returns an iterator to the next element, which you can use to continue the loop.
Functional approach using the range-v3 library (C++20)
[...] I'm looking for a solution to this or better way of removing elements.
Using the ranges::actions::remove_if action from the range-v3 library, you can use a functional programming style approach to mutate the object_list container in-place:
object_list |= ranges::actions::remove_if(
[](const auto& obj) { return obj.hit(); });
followed by subsequent ranges:for_each invocation to draw the object:
ranges::for_each(object_list, [](const auto& obj){ obj.draw(); });
DEMO.
You could do something like this:
for (size_t i = 0; i < object_list.size(); )
{
if (object_list[i].hit())
object_list.erase(object_list.begin() + i)
else
{
object_list[i].draw()
++i;
}
}
Let us say you are at i=5 and that object has been hit, after deleting that element, the obj at i=6 is shifted to i=5, and you haven't checked it, so just add i--; after your erase statement.
Another way to do it would be -
for(size_t i = 0; i < object_list.size();)
{
if(object_list[i].hit())
{
object_list.erase(object_list.begin() + i);
}
else
{
object_list[i].draw();
i++;
}
}
Also, it could possibly be faster to just remove the object from the vector where you execute the code that marks the object as hit, that way you just need to draw all the objects which are left out in the list. Some more background into how you are doing all this would be helpful to decide something specific which would be better :)
The shown code does not fail or give a vector subscript out of range - it just does not consider every object, as it skips over the element after the removed one.
For very short and concise solutions employing concepts from C++11 and later, see the answer by Equod or the one by dfri
For better understanding the issue, and/or if you have to stick to for loops with indices, you basically have two options:
Iterate over the vector in reverse direction (i.e. start at the last element), then items after the current one being shifted is not a problem;
for (int i=object_list.size()-1; i>=0; --i)
{
if (object_list[i].hit())
{
object_list.erase(object_list.begin() + i)
}
else
{
object_list[i].draw()
}
}
Or, if the order is important (as I could imagine with items to draw), and you have to iterate from front to back, then only increase the counter i if you have not erased the current element:
for (int i=0; i<object_list.size(); /* No increase here... */ )
{
if (object_list[i].hit())
{
object_list.erase(object_list.begin() + i);
}
else
{
object_list[i].draw();
++i; // ...just here if we didn't remove the element
}
}
I suspect that std::vector is not the container you want (but, of course, I don't know the entire code). Each call to erase implies reallocation of the right-part of the vector (and then copies of you objects), it could be very costly. And your actual problem is the symptom of a design problem.
From what I see, std::list is probably better:
std::list<object> objects;
// ...
for(std::list<object>::iterator it = objects.begin(); it != objects.end();)
{
if(it->hit())
objects.erase(it++); // No object copied
else
{
it->draw();
++it;
}
}

Deleting from std::list in a nested loop returns access violation

I have a large list of elements, with possible duplicates. I want to delete those duplicates, but my program results in an access violation error after deleting around 700 items.
Here is my code:
for (auto it : endlist){
bool first = true;
for (auto it2 : endlist){
if (!first){
if (similar(it, it2)){
endlist.remove(it2);
continue;
}
rotate( it);
if (similar(it, it2)){
endlist.remove(it2);
continue;
}
rotate(it);
if (similar(it, it2)){
endlist.remove(it2);
continue;
}
rotate(it);
if (similar(it, it2)){
endlist.remove(it2);
continue;
}
}
first = false;
}
}
The access violation is thrown in the second for loop. Can somebody explain why this happens?
Why don't you use
std::list::sort()
then
std::list::unique()
instead? It will get rid of all duplicates in a sorted list.
What you asked for:
for (size_t i=0; i!=endlist.size(); ++i)
{
for (size_t j=i+1; j!=endlist.size(); ++j)// only compare matrices once by using j=i+1
{
if (sometest(endlist[i],endlist[j]))
{
endlist.erase(endlist.begin()+j); // Also resizes the vector.
}
}
}
What you didn't ask:
If you have the ability to change your vector and elements according to your rotation this can be done cleaner with sorting.
For that you'll have to define an operator<(...) for your matrices, this should be possible by comparing their sizes and then comparing them lexicographically. Then you'll want to store the minimal matrix in terms of rotation in your endlist for this to make sense. Once that's done you can use the other answers approach for filtering.
And if you don't want to do anything with the duplicates anyway I'd recommend a container that doesn't allow duplicates from the beginning like a std::map.

unordered_set erase not working in C++

Consider the following situation
void first(){
unordered_set<int> validPorts;
int roundNum=0, preFunctionSize, postFunctionSize,j=0 ;
while(j <100){
if(some_condition_A){
validPorts.insert(some_int_value);
}
j++;
}
do{
preFunctionSize = validPorts.size();
second( validPorts, some_int_value );
postFunctionSize = validPorts.size();
}while(roundNum<12);
}
void second( unordered_set<int> & validPorts, int some_int_value ){
for (auto it = validPorts.begin(); it != validPorts.end();) {
if (it == validPorts.find(some_int_value)) {
validPorts.erase(it++); // <== CODE enters here, I checked
} else {
++it;
}
}
}
So I expect that the postFunctionSize should be less than the preFunctionSize since I know that it went till the erase function. But it looks like the erase function does not work since i get the same value for the two of them. I am not really sure whats happening here and what is causing it. Can you guys please help me out on what might be wrong with this?
Your code is pseudo of course in places but you need to do:
it = validPorts.erase( it );
in a loop where you are iterating through a collection erasing some of them.
However that is also not really what you want to do. You are trying to erase a value from your unordered_set so just do
validPorts.erase( some_int_value );
and no loop.

How to change a vector item in C++?

I've got a vector of structs in C++ and I would like to modify each item individually. I found that doing SomeStruct info = myVector[i] gives me a copy of the item, so if I modify it nothing will be changed. So right now I'm resetting the item like that: myVector[i] = info. Is there a more efficient way do that? One that won't involve a copy operation?
This is my current code:
struct CharacterInfo {
QChar character;
int occurrences;
double frequency;
};
std::vector<CharacterInfo> characterInfos;
// Some code to populate the vector
for (unsigned i = 0; i < characterInfos.size(); i++) {
CharacterInfo info = characterInfos[i];
info.frequency = (double)info.occurrences / (double)totalOccurrences;
characterInfos[i] = info; // how to avoid this?
}
The simplest way which doesn't change too much of your code is just to use a reference instead of an instance. So:
SomeStruct & info = myVector[i];
The next easiest way is to change from using a loop with an index, so like:
for (std::vector<SomeStruct>::iterator it = myVector.begin(); it != myVector.end(); ++it)
{
SomeStruct & info = *it;
// do stuff here
}
With the STL you can go even further, especially if you have a C++11 capable compiler, for instance:
std::for_each(std::begin(myVector), std::end(myVector), [](SomeStruct & info) { /* do stuff here */ });
Also not related to your question directly, but if you add a method to the struct that computes the frequency, the code becomes much cleaner, for instance following from the last example you could do:
std::for_each(std::begin(myVector), std::end(myVector), std::mem_fun(&SomeStruct::calculateFrequency));
This will also work without a C++11 compiler if you change the calls to std::begin(myVector) with myVector.begin() and the same for end.
You can use a reference:
CharacterInfo& info = characterInfos[i];
info.frequency = (double)info.occurrences / (double)totalOccurrences;
The reference info is bound to the element of your vector. If you change it, you change
the vector element too.
You could iterate through the vector with an STL iterator:
for (vector<CharacterInfo>::iterator it = characterInfos.begin();
it != characterInfos.end(); ++it) {
it->frequency = (double)it->occurrences / totalOccurrences;
}
In the loop, it is an iterator that has basically same functionality and interface as a pointer to a CharacterInfo struct: http://cplusplus.com/reference/std/iterator/RandomAccessIterator/
Looping with an iterator is the more idiomatic way of iterating through each element of a std::vector if you don't need to know the index of each element.
I am not sure I understand your question but I think you are trying to do this?
for (unsigned i = 0; i < characterInfos.size(); i++) {
characterInfos[i].frequency = (double)characterInfos[i].occurrences / (double)totalOccurrences;
}
Another option would be to use iterators:
for(std::vector<CharacterInfo>::iterator it = characterInfos.begin(); it != characterInfos.end(); ++it){
it->frequency = (double)it->occurences / (double)totalOccurences;
}
Wow, this is a very old question. For "newer" c++, the same can be done with Range-based for loop (since C++11)
for(auto &characterInfo : characterInfos) {
characterInfo.frequency = characterInfo.occurences / static_cast<double>(totalOccurences);
}

C++ program halts after dynamic memory allocation

I'm having problem with a copying method in a simple C++ program.
Everytime I call copy:
Sudoku::SudokuNode** Sudoku::copy(SudokuNode** sudokuBoard)
{
SudokuNode** tempSudokuBoard = new SudokuNode*[9];
for(int i = 0; i<9; i++)
{
tempSudokuBoard[i] = new SudokuNode[9];
for(int j = 0; j<9; j++)
{
tempSudokuBoard[i][j].currentInteger = sudokuBoard[i][j].currentInteger;
for(vector<int>::iterator iter = sudokuBoard[i][j].possibleIntegers.begin(); iter!= sudokuBoard[i][j].possibleIntegers.end();)
{
tempSudokuBoard[i][j].possibleIntegers.push_back(*iter);
}
}
}
return tempSudokuBoard;
}
The program seems to completely halt, not returning a a visible error.
If I try to debug the program, the debugger works fine until I arrive at the copy method. Then the debugger displays a dialog box saying:
There is no source code available for the current location.
Any idea what is wrong?
for(vector<int>::iterator iter = sudokuBoard[i][j].possibleIntegers.begin(); iter!= sudokuBoard[i][j].possibleIntegers.end();)
You don't seem to be advancing the iterator in that loop, so it will never end. Add ++iter to the counting expression (after the last ; in the for loop).
As to why your debugger can't find source for that location, that's platform dependent. What debugger are you using?
You do not increase the iterator on the inside loop:
for(vector<int>::iterator iter = sudokuBoard[i][j].possibleIntegers.begin(); iter!= sudokuBoard[i][j].possibleIntegers.end(); ++iter)
Resulting an infinate for loop (Compiler knows this and "optimized" it for an infinite loop, which is why there is no code available).
I suggest you recode it to use ...
class SudokuBoard
{
SudokuNode nodes[9];
};