I have the following function.
void BulletFactory::update(Uint32 ticks) {
std::list<Sprite*>::iterator it = activeBullets.begin();
while (it != activeBullets.end()) {
(*it)->update(ticks);
if(!((*it)->inView())) {
activeBullets.remove(*it);
Sprite* temp = *it;
it++;
inactiveBullets.push_back(temp);
} else {
it++;
}
}
}
when the condition !((*it)->inView()) is being true there is a segmentation fault. I am not able to see the problem.
edit: forgot to mention that activeBullets and inactiveBullets are two lists.
activeBullets.remove(*it);
Sprite* temp = *it; //<--- problem
it++; //<-- problem
should be:
Sprite* temp = *it;
it = activeBullets.erase(it); //use erase
//it++; don't increment it
You must not modify the element that the iterator is pointing to, because it invalidates the iterator. See this question for solutions.
Related
I'm a bit rusty on c++ and am returning to it to get better. I searched a bit on StackOverflow already to no avail. The issue I am having is "list iterator not incrementable". Below is the section I believe to be the culprit, as this is the only location that I use an iterator with a modifier.
if (!io_queue.empty()) {
for (list<Process>::iterator it = io_queue.begin(); it != io_queue.end(); ++it) {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
it = io_queue.erase(it);
}
else {
ready_queue.push_back(*it);
it = io_queue.erase(it);
}
}
else {
it->decBurst();
}
}
}
You are incrementing it unconditionally on every loop iteration. But when erase() is called, it returns a new iterator to the next list element after the one being erased, and so if that results in it being set to the end iterator than incrementing it again is undefined behavior.
Simply move the ++it from the for statement to the else block inside the loop.
Try this:
if (!io_queue.empty()) {
list<Process>::iterator it = io_queue.begin();
do {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
}
else {
ready_queue.push_back(*it);
}
it = io_queue.erase(it);
}
else {
it->decBurst();
++it; // <— moved here
}
}
while (it != io_queue.end());
}
I am new in programming c++, so please don't be angry with me if my source code is not exactly brilliant.
I have to write a programm to handle with nodes and edges in a graph for my studies.
I have 2 std::lists in my source code. The first one is to store general Nodes and the other one for saving the kind class of my nodes called ArticleNodes. In general, all elements are pointers to the created objects.
To figure out whether one object is the same in the other list I save the memory address and compare it to the elements on the second list. If there is a match the second element will be deleted.
Now I'd like to delete one element in both lists:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) { m_articlenode.remove(pCurrentArticleNode); }
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
I can compile this, but when I call the function, my programm just exits with return 1.
Actually, I figured out that the remove-command in the if-clause is the problem. Why does that not work??
You should use algorithms more than doing everything manually:
void Graph::deleteNode(unsigned int nodeNumber)
{
assert (nodeNumber < m_nodes.size());
auto it = std::next( m_nodes.begin(), nodeNumber - 1 );
auto itArticle = std::find( m_articlenode.begin(), m_articlenode.end(), *it );
if( itArticle != m_articlenode.end() )
m_articlenode.erase( itArticle );
delete *it;
m_nodes.erase(it);
}
Btw your code deletes the same object twice.
When you remove an element from std::list object with remove( ) method, all iterators pointing to that elements become invalid. In your case, after you remove an element from the list m_articlenode, the iterator object itArticle becomes invalid. And when you increment that iterator, you get an undefined behavior.
Pay attention that the method remove( ) deletes all of the items in the list with given value. So you don't need the for-loop at all. Here is the fixed version of your function:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
m_articlenode.remove(pCurrentNode);
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
}
You have to use the erase method of std::list in order to remove an element from your list while iterating over it.
This should do the trick:
void Graph::deleteNode(unsigned int nodeNumber)
{
list<Node*>::iterator it = m_nodes.begin();
ArticleNode* pCurrentArticleNode;
for(unsigned int i=1; i<nodeNumber; i++) { it++; }
Node* pCurrentNode = (*it);
list<ArticleNode*>::iterator itArticle = m_articlenode.begin();
while(itArticle != m_articlenode.end()) {
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode) {
m_articlenode.erase(itArticle++);
} else {
itArticle++;
}
}
m_nodes.remove(pCurrentNode);
delete pCurrentNode;
delete pCurrentArticleNode;
}
Simple issue in your code is that if you go into the if condition is met and it's body executed, you should come out of the loop. std::remove invalidates the iterator ann you will get issues in next iteration so do:
for (list<ArticleNode*>::iterator itArticle = m_articlenode.begin(); itArticle != m_articlenode.end(); itArticle++)
{
pCurrentArticleNode = (*itArticle);
if(pCurrentNode==pCurrentArticleNode)
{
m_articlenode.remove(pCurrentArticleNode);
break;
}
}
In general, there are other issues in the code. As a first step I would suggest using shared_ptr for managing your Nodes and have list of shared_ptr instead of list of pointers.
I am new in programming c++, so please don't be angry with me if my
source code is not exactly brilliant.
We all start somewhere.
Now I'd like to delete one element in both lists:
OK. Quick question. Why are you deleting article node if it is already deleted (via base Node)? I'm assuming for now node is not duplicated in the list:
My solution below... I've passed the lists as arguments. See comments:
#include <list>
#include <algorithm>
struct Node
{
virtual ~Node(){} //For dyna cast to work...
};
struct ArticleNode : Node
{
};
void deleteNode(std::list<ArticleNode*>& articleList, std::list<Node*>& m_nodes, unsigned int nodeNumber)
{
using namespace std;
if (m_nodes.size() > nodeNumber)
{
auto it = m_nodes.begin();
// Advance advances our iterator by N. No need for your for loop - less risk...
std::advance(it,nodeNumber);
Node* currentNode = *it;
//Casting is bad here, but hey, lets assume if type is wrong, we only erase
// it from Node...(Your call)?
ArticleNode* currentArticleNode = dynamic_cast<ArticleNode*>(currentNode);
if (currentArticleNode)
{
//Use find here.... KISS
auto foundPos = std::find(articleList.begin(), articleList.end(), currentArticleNode);
if (foundPos != articleList.end())
{
//No need to delete currentArticleNode, as we're deleting it already...
articleList.erase(foundPos);
}
//Assuming only one item for now...
}
//Else our node was obviously not the right type, and cannot exist in articleNodes...
m_nodes.erase(it);
delete currentNode;
}
else
{
std::cout << "No such node: " << nodeNumber << std::endl;
}
}
I am having an issue and I think it is because of the iterators being invalidated. However I use the iterator from erase() to resume iterating other the structure. When erase() when I try to increment after erase() is called the first time I get the following error
'vector iterator not incrementable '
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
}
}
}
Can anyone see where I am going wrong?
erase returns an iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
so if you erase you do not need to increment your iterator
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); )
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
else
{
++itVector
}
}
This solved my issue, I just have to call break after i erase but once i erase i do not need to loop to the end of the list. (#Aleexander solution also works)
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
break;
}
}
}
}
I have a class Circle whose instances I keep track of with these:
Circle *f1;
vector<Circle> list;
vector<Circle>::iterator it;
I have managed to create multiple Circles and got them to move around. How can I erase a specific instance of Circle? For example, if a certain circle hits a wall, then it should be erased. I've looked around at other questions and I even tried the code they gave out and no luck. Here's what I've got at the moment:
for (it = list.begin(); it != list.end(); ++it) {
it->x += 1;
if (it->x == ofGetWindowWidth()) {
list.erase(it);
}
}
I have gotten other statements to work with the if statement such as reversing the direction of their movement. list.erase(it); was a line of code I got from here and I don't understand why it crashes my program.
for (it = list.begin(); it != list.end(); /* nothing here */) {
it->x += 1;
if (it->x == ofGetWindowWidth()) {
it = list.erase(it);
} else {
++it;
}
}
The problem with your original code is that erasing an element invalidates the iterator to that element - the very same iterator you are trying to increment next. This exhibits undefined behavior.
list.erase invalidates iterators to the erased element. Therefore, after you erase the element pointed to by "it", "it" is invalidated and the ++it, which follows after the for loops body, can crash your program.
Rewriting your code to something similiar to the following should prevent your crash:
for(it=list.begin();it!=list.end(); ) {
//your code
if(it->x==ofGetWindowWidth())
it=list.erase(it);
else
++it;
}
The problem with the above code using erase() is that it invalidates the content of it when the element is being erase. You can use, e.g., this instead:
for (it = list.begin(); it != list.end(); ) {
it->x += 1;
if (it->x == ofGetWindowWidth()) {
list.erase(it++);
}
else {
++it;
}
}
The branch using erase() moves the kept iterator it off its current location before erase()ing the element. Only the temporary object return from it++ gets invalidated. Of course, for this loop to work, you can't unconditionally increment it, i.e., the non-erase()ing branch needs its own increment.
You could use erase with remove_if. This also works for removal of multiple elements. In your case it's
list.erase(std::remove_if(list.begin(), list.end(),
[](const Circle& c){return c.x == ofGetWindowWidth();},list.end()),
Example with integers:
#include <algorithm>
#include <vector>
#include <iostream>
int main()
{
std::vector<int> str1 = {1,3,5,7};
str1.erase(std::remove_if(str1.begin(), str1.end(),
[](int x){return x<4 && x>2;}), str1.end());
for(auto i : str1) std::cout << i ;
}
prints 157
i'm initializing and inserting into a list like so
_ARRAY_DETAIL* pAR = new _ARRAY_DETAIL;
pAR->sVar1 = 1;
pAR->nVar2 = 2;
m_SomeList.push_back(pAR);
i am trying to find and erase all from the list that contains the value 1, and then delete the pointer we created with new, is my example below doing both in a good, correct efficient way?
while(Iter != m_SomeList.end());
{
if((*Iter)->sVar1 == 1)
{
_ARRAY_DETAIL* pAR = *Iter;
Iter = m_SomeList.erase(Iter);
delete pAR; pAR = NULL;
}
Iter++;
}
Once you erase the iterator, it's no longer valid. You need to increment it before the erase.
if((*Iter)->sVar1 == 1)
{
_ARRAY_DETAIL* pAR = *Iter;
m_SomeList.erase(Iter++);
delete pAR;
}
else
++Iter;
You were correct that erase returns an incremented iterator but I prefer to do it explicitly, before the iterator is erased.
Setting pAR to NULL is redundant, since it's going out of scope on the next line anyway.
Also note that you should only increment Iter if you didn't increment it in the other part of the if.
as an alternative you could use remove if although what you have done seems fine.
bool IsOne (_ARRAY_DETAIL* pAR) {
if(pAR->sVar1 == 1) {
delete pAR;
return true;
}
return false;
}
remove_if (vec.begin(), vec.end(), IsOne);