std::vector<int *>intList;
int *a = new int;
*a = 3;
int *b = new int;
*b = 2;
int *c = new int;
*c = 1;
intList.push_back(a);
intList.push_back(b);
intList.push_back(c);
std::vector<int *>::iterator it;
for (it = intList.begin(); it != intList.end();)
{
int *a = (int*)*it;
if ((*a) == 2)
{
delete a;
intList.erase(it);
}
else
{
++it;
}
}
This code crashes at the start of the for loop when something is erased from the vector. I do not understand why. I am using Visual Studio 2015 if that helps
erase returns next iterator.
if ((*a) == 2)
{
delete a;
it = intList.erase(it);
}
EDIT:
remove() and remove_if() will copy the elements(pointer here) and one will end up with multiple elements pointing to same integer and if you then try to free them, you'll be left with dangling pointers.
Consider the vector has 4 elements which look something like
0x196c160 0x196bec0 0x196c180 0x196bee0
One might be tempted to use erase-remove idiom
auto temp = remove_if(vec.begin(),vec.end(),[](const auto &i){return *i==2;});
Now it looks like
0x144aec0 0x144b180 0x144b180 0x144aee0
temp would be pointing to 3rd element and a
for(auto it=temp;it!=vec.end();it++)
delete *it;
Now the second element is a dangling pointer.
EDIT 2:
The above problem could be solved if you delete before the element is copied.Look at #Dietmar's answer.
Better to use std::vector<std::unique_ptr<int>> (or even std::vector<int> if you don't need pointer).
then just use erase-remove idiom:
std::vector<int> intList{3, 2, 1};
intList.erase(std::remove(intList.begin(), intList.end(), 2), intList.end());
or
std::vector<std::unique_ptr<int>> intList;
intList.puch_back(std::make_unique<int>(3));
intList.puch_back(std::make_unique<int>(2));
intList.puch_back(std::make_unique<int>(1));
intList.erase(std::remove_if(intList.begin(), intList.end(),
[](const auto& p){ return *p == 2; }),
intList.end());
If you really need raw owning pointer, you may use a variant using partition:
std::vector<int*> intList{ new int {3}, new int {2}, new int{1} };
auto it = std::partition(intList.begin(), intList.end(),
[](const auto& p){ return *p != 2; });
std::foreach (it, intList.end(), [](int* p) { delete p; });
intList.erase(it, intList.end());
Finally, if you really need to do it manually, you have to fix your erase line to:
it = intList.erase(it);
to have:
std::vector<int*> intList{ new int {3}, new int {2}, new int{1} };
for (auto it = intList.begin(); it != intList.end(); /*Empty*/) {
int *p = *it;
if (*p == 2) {
delete p;
it = intList.erase(it);
} else {
++it;
}
}
That code is causing iterator invalidation.
You're deleting a then expecting your iterator (which is just a pointer) to know what just happened.
If you have to iterate through the whole vector then consider a temporary vector of what to keep/throwaway and use that.
Or better yet just use find
http://www.cplusplus.com/reference/algorithm/find/
The simple anser is: you don’t! Instead you use a two stage approach: first get rid of the elements, then resize the container. The use of raw pointers slightly complicates things but it is still doable:
auto end = std::remove_if(intList.begin(), intList.end(),
[](int *ptr){ return *ptr == 2 && (delete ptr, true); });
intList.erase(end, endList.end());
Trying to erase individual elements while iterating over std::vector has non-linear worst case complexity.
If you take a look at documentation the erase function returns next iterator. In your case using it = intList.erase(it) is the solution. After c++11 all erase functions from other containers follow the same idea.
Related
std::vector<int *>intList;
int *a = new int;
*a = 3;
int *b = new int;
*b = 2;
int *c = new int;
*c = 1;
intList.push_back(a);
intList.push_back(b);
intList.push_back(c);
std::vector<int *>::iterator it;
for (it = intList.begin(); it != intList.end();)
{
int *a = (int*)*it;
if ((*a) == 2)
{
delete a;
intList.erase(it);
}
else
{
++it;
}
}
This code crashes at the start of the for loop when something is erased from the vector. I do not understand why. I am using Visual Studio 2015 if that helps
erase returns next iterator.
if ((*a) == 2)
{
delete a;
it = intList.erase(it);
}
EDIT:
remove() and remove_if() will copy the elements(pointer here) and one will end up with multiple elements pointing to same integer and if you then try to free them, you'll be left with dangling pointers.
Consider the vector has 4 elements which look something like
0x196c160 0x196bec0 0x196c180 0x196bee0
One might be tempted to use erase-remove idiom
auto temp = remove_if(vec.begin(),vec.end(),[](const auto &i){return *i==2;});
Now it looks like
0x144aec0 0x144b180 0x144b180 0x144aee0
temp would be pointing to 3rd element and a
for(auto it=temp;it!=vec.end();it++)
delete *it;
Now the second element is a dangling pointer.
EDIT 2:
The above problem could be solved if you delete before the element is copied.Look at #Dietmar's answer.
Better to use std::vector<std::unique_ptr<int>> (or even std::vector<int> if you don't need pointer).
then just use erase-remove idiom:
std::vector<int> intList{3, 2, 1};
intList.erase(std::remove(intList.begin(), intList.end(), 2), intList.end());
or
std::vector<std::unique_ptr<int>> intList;
intList.puch_back(std::make_unique<int>(3));
intList.puch_back(std::make_unique<int>(2));
intList.puch_back(std::make_unique<int>(1));
intList.erase(std::remove_if(intList.begin(), intList.end(),
[](const auto& p){ return *p == 2; }),
intList.end());
If you really need raw owning pointer, you may use a variant using partition:
std::vector<int*> intList{ new int {3}, new int {2}, new int{1} };
auto it = std::partition(intList.begin(), intList.end(),
[](const auto& p){ return *p != 2; });
std::foreach (it, intList.end(), [](int* p) { delete p; });
intList.erase(it, intList.end());
Finally, if you really need to do it manually, you have to fix your erase line to:
it = intList.erase(it);
to have:
std::vector<int*> intList{ new int {3}, new int {2}, new int{1} };
for (auto it = intList.begin(); it != intList.end(); /*Empty*/) {
int *p = *it;
if (*p == 2) {
delete p;
it = intList.erase(it);
} else {
++it;
}
}
That code is causing iterator invalidation.
You're deleting a then expecting your iterator (which is just a pointer) to know what just happened.
If you have to iterate through the whole vector then consider a temporary vector of what to keep/throwaway and use that.
Or better yet just use find
http://www.cplusplus.com/reference/algorithm/find/
The simple anser is: you don’t! Instead you use a two stage approach: first get rid of the elements, then resize the container. The use of raw pointers slightly complicates things but it is still doable:
auto end = std::remove_if(intList.begin(), intList.end(),
[](int *ptr){ return *ptr == 2 && (delete ptr, true); });
intList.erase(end, endList.end());
Trying to erase individual elements while iterating over std::vector has non-linear worst case complexity.
If you take a look at documentation the erase function returns next iterator. In your case using it = intList.erase(it) is the solution. After c++11 all erase functions from other containers follow the same idea.
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;
}
}
Im trying do delete a object pointer in a vector when a user wants to delete an object. Im trying to:
Delete the pointer
Remove the pointer from the vector
I have in a DataBase class a map of strings to vectors. And the vectors are containing pointers to objects. Like this:
Definition of objectMap and objectector in "database.h"
private:
typedef std::vector<Object *> objectVector;
typedef std::map<std::string, objectVector> objectMap;
objectMap objects;
Implementation of deleteId in "database.cpp"
bool DataBase::deleteId(int id)
{
// vectors
for(objectMap::iterator vec = objects.begin(); vec != objects.end(); ++vec)
{
objectVector v = vec->second;
// objects
for(objectVector::iterator obj = v.begin(); obj != v.end(); ++obj)
{
if((*obj)->getId() == id)
{
delete *obj; // pointer
*obj = 0;
v.erase(obj); // erase from vector
modified = true;
return true;
}
}
}
return false;
}
Im currently trying to the debug the program, and I am printing out the whole map of objects to see if the deletion worked. The problem is that the object pointer is still there after the deletion even if the function confirmed the deletion (i.e returned true). Can someone please explain whats going on?
objectVector v = vec->second;
This copies the vector by value and thus you're referring to the copy, not to the real vector.
Suggesting to change that line into
objectVector& v = vec->second; // Reference to the "real" vector
No problems in invalidating your obj iterator since you're quitting the function as soon as you encounter the deletion element.
Your code is wrong at least because you did not erase an element in the original vector. Use reference to the vector
for(objectMap::iterator vec = objects.begin(); vec != objects.end(); ++vec)
{
objectVector &v = vec->second;
Also consider an approach when if after the erasing of an element in a vector the vector becomes empty then maybe you shoulod also erase the corresponding element in the map.
For example )without testing)
bool DataBase::deleteId(int id)
{
bool modified = false;
for ( auto it1 = objects.begin(); !modified && it1 != objects.end(); ++it1 )
{
auto it2 = std::find_if( it1->second.begin(), it1->second.end(),
[&]( Object *o ) { return ( o->getId() == id ); } );
modified = it2 != it1->second.end();
if ( modified )
{
delete *it2;
it1->second.erase( it2 );
if ( it1->second.empty() ) it1 = objects.erase( it1 );
}
}
return modified;
}
I am wondering what the fast swap method is in C++ containers such as list and vector, because I haven't found any builtin swap functions yet.FYI, I want to swap object rather than the whole list.
For example, assume we have such int sequence 3 2 4 5 and they are stored in a list container(stl), and I want to swap 2 and 4. Here is the dumb method that I came up with:
list<int> numbers;
numbers.push_back(3);
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(5);
list<int>::iterator item;
item=numbers.begin();
advance(item,2);
int key = *item;
advance(item,-1);
numbers.insert(item,key);
advance(item,1);
numbers.erase(item);
So, briefly speaking, what I am doing here is just "Copy, insert, and remove", and the reason why I do this is that I heard list container is very efficient for inserting and removing elements,but I am pretty sure there should be better algorithms.In addition, I also heard there exists a constant time swap method related to pointers, so anyone knows anything about it?
Thanks for helping out.
You want std::swap:
list<int>::iterator item1 = numbers.begin();
++item1;
list<int>::iterator item2 = item1;
++item2;
std::swap(*item1, *item2);
Use iter_swap to swap the elements pointed at by two iterators to the list. This swaps the data, rather than the nodes, but it's easy.
#include <list>
#include <iostream>
int main() {
std::list<int> numbers;
numbers.push_back(3);
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(5);
auto first = std::next(numbers.begin(), 2);
auto second = std::next(numbers.begin(), 1);
std::iter_swap(first, second);
for(int& v : numbers)
std::cout << v << ' ';
}
http://coliru.stacked-crooked.com/view?id=a89b3b1ae9400367b6ff194d1b504e58-f674c1a6d04c632b71a62362c0ccfc51
If you want to swap nodes rather than elements, you can use list::splice, though it's a little tricker:
int main() {
std::list<int> numbers;
numbers.push_back(3);
numbers.push_back(2);
numbers.push_back(4);
numbers.push_back(5);
std::list<int> temporary;
auto move_from = std::next(numbers.begin(), 2);
temporary.splice(temporary.begin(), numbers, move_from, std::next(move_from));
auto move_to = std::next(numbers.begin(), 1);
numbers.splice(move_to, temporary);
for(int& v : numbers)
std::cout << v << ' ';
}
It seems you may be looking for a way to move around nodes within the list, without copying actual elements. You could do this with list::splice. Nothing like that is possible for vector, of course, which is not node-based.
Something like this:
list<int>::iterator to = numbers.begin();
++to;
list<int>::iterator which = to;
++which;
numbers.splice(to, numbers, which);
How about using swap?
using std::swap;
swap(numbers[1], numbers[2]);
which will use std:swap or ADL to determine a proper swap-function if there is one defined for the arguments.
As #Mooing Duck correctly pointed out std::list requires you to use iterators.
std::iter_swap(numbers.begin()+1, numbers.begin()+2);
You can also use
using std::swap;
std::list<int>::iterator item(numbers.begin());
std::advance(item, 1);
std::list<int>::iterator other(item);
std::advance(other, 1);
swap(*item, *other);
or
using std::swap;
swap(*std::next(numbers.begin(), 1), *std::next(numbers.begin(), 2));
or
std::iter_swap(std::next(numbers.begin(), 1), std::next(numbers.begin(), 2));
With std::swap(),
int reverse(std::list<int>& list){
int exchange = 0;
int move = 0;
int distance_lo = 0;
int distance_hi = 0;
std::list<int>::iterator it_lo = list.begin();
std::list<int>::iterator it_hi = --list.end();
while (1) {
it_lo = list.begin();
it_hi = --list.end();
std::advance(it_lo, move);
std::advance(it_hi, -move);
distance_lo = std::distance(list.begin(), it_lo);
distance_hi = std::distance(list.begin(), it_hi);
if (distance_lo < distance_hi) {
std::swap(*it_lo, *it_hi);
exchange++;
} else {
break;
}
move++;
}
return exchange; }
With std::list::splice(),
int reverse(std::list<int>& list) {
int exchange = 0;
int move = 0;
int distance_lo = 0;
int distance_hi = 0;
std::list<int>::iterator it_lo = list.begin();
std::list<int>::iterator it_hi = --list.end();
while (1) {
it_lo = list.begin();
it_hi = --list.end();
std::advance(it_lo, move);
std::advance(it_hi, -move);
distance_lo = std::distance(list.begin(), it_lo);
distance_hi = std::distance(list.begin(), it_hi);
if (distance_lo < distance_hi) {
std::list<int> tmp;
tmp.splice(tmp.begin(), list, it_lo);
tmp.splice(std::next(tmp.begin(),1), list, it_hi);
it_lo = list.begin();
it_hi = --list.end();
std::advance(it_lo, move); //insert before it_lo
std::advance(it_hi, -move);
std::advance(it_hi, 1); //insert after it_hi
list.splice(it_lo, tmp, std::next(tmp.begin(),1));
list.splice(it_hi, tmp, tmp.begin());
exchange++;
} else {
break;
}
move++;
}
return exchange; }
Hope it could help you :)
I have this loop
for(int i=0;i<vec1.size();++i)
{
if(vec1[i]==*p)
{
vec1[i]=*p;
cout<<"element updated"<<endl;
}
else
{
cout<<"push_back"<<endl;
vec1.push_back(*p);
}
}
I'm inserting objects in container class and I've overloaded the == to check two parameters inside the object and if they match I want to update the them and if they don't match I want to put them in the vector, but I don't seem to be able to properly populate my vector, when I do vec1.size() I get 0 even when I insert 3 objects.
You're problem is that your if is inside your search loop. Your if will never be executed, because your loop body never runs, because your .size() will never be greater than 0.
Try this:
// UNTESTED
std::vector<person> vec1;
add(person *p) {
std::vector<person>::iterator it = std::find(vec1.begin(), vec1.end(), *p);
if(it == vec1.end())
vec1.push_back(*p);
else
*it = *p;
}
Or, if you really want to code the loop by hand:
// UNTESTED
std::vector<person> vec1;
add(person *p) {
int i;
for(i=0;i<vec1.size();++i) {
if(vec1[i] == *p)
break;
}
if(i == vec1.size())
vec1.push_back(*p);
else
vec1[i] = *p;
}
Of course, you might consider changing your container. Using a std::map would shorten your code and reduce the time it takes to manipulate large data sets.
std::map<std::string, person> map1;
add(person *p) {
map1[p->name] = *p;
}
When the vec1 starts from empty, the for loop is not going to run. So you want to have at least one element in vec1 to start with. How about add this:
vec1.push_back(*p);
for(int i=0;i<vec1.size();++i){//the rest}