I supose there is a problem with the iterators, but I can't understand why!
Could you please give me some more info?
I have the movie class with the following:
public:
vector<string> casting() const;
private:
string _titol;
short _year;
vector<string> _alies;
string _director;
vector<string> _casting;
The casting() method just returns the _casting vector
I have my main.cpp where I call:
void Movies::actorMovies(string actor){
bool existeix = false;
std::map<titleyear,Movie>::iterator it = _pelis.begin();
std::vector<string>::iterator it2;
for(it; it!=_pelis.end(); it++){
for(it2=it->second.casting().begin(); it2!=it->second.casting().end(); it2++){
/*if((*it2).compare(actor)==0){
cout<<"Titol: "<<it->first.t<<endl<<"Any: "<<it->first.y<<endl;
existeix = true;
}*/
}
}
if(!existeix)
cout<<"NOT FOUND"<<endl;
}
The Segmentation fault comes when i want Uncomment the if statement.
Can someone see what's happenning here?
Your premise is wrong. The casting() method returns a new copy of the _casting vector every time it is called. Thus it2 can never equal it->second.casting().end(), since it's an iterator to a completely different container!
In fact, it2 is immediately invalidated at the end of the full expression, since it's an iterator into a temporary container that dies immediately.
If casting() is meant to provide a view of the actual _casting vector, it should return an lvalue:
const std::vector<std::string> & casting() const { return _casting; }
// ^^^
The casting() function returns a temporary vector by value. Different calls to the function each return a new copy of the vector.
So when you write:
for(it2=it->second.casting().begin();
the it2 is immediately dangling because the temporary vector now gets destroyed.
The test it2!=it->second.casting().end(); compares a dangling iterator with the end of a new copy of _casting, causing undefined behaviour.
There are two options to fix this:
Store the result: vector<string> casting = it->second.casting(); for (it2 = casting.begin(); ..........
Make casting() return by reference, so that you will work on the actual instance of the vector inside it->second.
Related
I have this method:
route_departure_const_iterator departure_at(platform_route_const_reference pr, packed_time tm)
{
std::vector<route_departure_object> v;
for (auto&& e : pr.departures) {
int i = 0;
while (true)
{
if (pr.tt->timetable_route_departures.at(i).platform_name == pr.platform_name && pr.tt->timetable_route_departures.at(i).route_name == pr.route_name && pr.tt->timetable_route_departures.at(i).departure_time >= e)
{
route_departure_const_reference rd = pr.tt->timetable_route_departures.at(i);
v.push_back(rd);
break;
}
i++;
}
}
return v.begin();
}
My question is, if I initialized new vector of objects in this method, when I return the iterator, will it point at this vector or will this vector be deleted after exiting the method?
Then, pr.tt->timetable_route_departures is vector of route_departure_object like vector v.
route_departure_const_reference rd is a reference to route_departure_object. Will my vector v consists of the original objects by references or they will be references on new objects?
With this method, when I will use the returned iterator outside this method, on vector of references to the same objects, will it work?
I mean, I have this method:
route_departures_const_reference departures(platform_route_const_reference pr)
{
std::vector<route_departure_object> v;
for (auto&& e : pr.departures) {
int i = 0;
while (true)
{
if (pr.tt->timetable_route_departures.at(i).platform_name == pr.platform_name && pr.tt->timetable_route_departures.at(i).route_name == pr.route_name && pr.tt->timetable_route_departures.at(i).departure_time == e)
{
route_departure_const_reference rd = pr.tt->timetable_route_departures.at(i);
v.push_back(rd);
break;
}
i++;
}
}
return v;
}
There are many more objects in pr.tt->timetable_route_departures, I just choose some of them. Then, when I will use returned iterator on this returned vector, will it point at the exact same chosen objects in pr.tt->timetable_route_departures?????
Thanks a lot for help!
if I initialized new vector of objects in this method, when I return the iterator, will it point at this vector or will this vector be deleted after exiting the method?
The vector will be destroyed when it goes out of scope when the function exits, thus the returned iterator will be left dangling.
Will my vector v consists of the original objects by references or they will be references on new objects?
It will contain copies of the original objects.
when I will use the returned iterator outside this method, on vector of references to the same objects, will it work?
No. Because one, you don't have a vector of references, but a vector of copied objects. And two, the vector was already destroyed and you have a dangling iterator.
route_departures_const_reference departures(platform_route_const_reference pr)
If route_departures_const_reference is actually a reference (ie, std::vector<route_departure_object>&) as its name suggests, then you would be returning a dangling reference to a destroyed vector.
when I will use returned iterator on this returned vector, will it point at the exact same chosen objects in pr.tt->timetable_route_departures?????
No, it would be referring to the copied objects that were made (if the returned vector/iterator were valid to begin with).
I have a vector of object pointers that I am adding to and deleting from while looping through to update objects. I can't seem to delete objects that have "died" from the vector without causing a memory error. I'm not really sure what I'm doing wrong. Listed below is my update method and it's sub method.
void Engine::update(string command){
if(getGameOver()==false){
for(p=objects.begin();p!=objects.end();p++){
spawnUpdate(command);
//cout<<"Spawn"<<endl;
objectUpdate(command);
//cout<<"object"<<endl;
scrollUpdate(command);
// cout<<"scroll"<<endl;
killUpdate(command);
//cout<<"kill"<<endl;
}
}
}
void Engine::killUpdate(std::string command){
if((*p)->getIsDead()==true){delete *p;}
}
void Engine::objectUpdate(string command){
(*p)->update(command,getNumObjects(),getObjects());
if(((*p)->getType() == PLAYER)&&((*p)->getPosX()>=getFinishLine())){setGameOver(true);}
}
void Engine::scrollUpdate(string command){
//Check player position relative to finishLine
if(((*p)->getType() == PLAYER)&&((*p)->getPosX()>(SCREEN_WIDTH/2))){
(*p)->setPosX((*p)->getPosX()-RUN_SPEED);
setFinishLine(getFinishLine()-RUN_SPEED);
for(q=objects.begin();q!=objects.end();q++){
//Shift objects to pan the screen
if((*q)->getType() == OPPONENT){(*q)->setPosX((*q)->getPosX()-RUN_SPEED);}
if((*q)->getType() == BLOCK){(*q)->setPosX((*q)->getPosX()-RUN_SPEED);}
}
}
}
void Engine::spawnUpdate(string command){
if(command.compare("shoot")==0){
cout<<"Bang!"<<endl;
if((*p)->getType() == PLAYER){objects.push_back(new Bullet((*p)->getPosX(),(*p)->getPosY(),(*p)->getState()));cout<<"Bullet success "<<endl;}
}
}
Some assumptions/definitions:
objects a member variable, something like vector<Object*> objects;
p is also a member variable, something like vector<Object*>::iterator p;
So p is an iterator, *p is an Object pointer, and **p is an Object.
The problem is that this method:
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
}
}
deallocates the Object pointed to by *p, the pointer in the vector at the position referenced by the p iterator. However the pointer *p itself is still in the vector, now it just points to memory that is no longer allocated. Next time you try to use this pointer, you will cause undefined behavior and very likely crash.
So you need to remove this pointer from your vector once you have deleted the object that it points to. This could be as simple as:
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
objects.erase(p);
}
}
However, you are calling killUpdate from update in a loop that iterates over the objects vector. If you use the code above, you will have another problem: once you erase p from the objects vector, it is no longer safe to execute p++ in your for-loop statement, because p is no longer a valid iterator.
Fortunately, STL provides a very nice way around this. vector::erase returns the next valid iterator after the one you erased! So you can have the killUpdate method update p instead of your for-loop statement, e.g.
void Engine::update(string command) {
if (getGameOver() == false) {
for (p = objects.begin(); p != objects.end(); /* NOTHING HERE */) {
// ...
killUpdate(command);
}
}
}
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
p = objects.erase(p);
} else {
p++;
}
}
This is of course assuming that you always call killUpdate in the loop, but I'm sure you can see the way around this if you don't -- just execute p++ at the end of the for-loop body in the case that you haven't called killUpdate.
Also note that this is not particularly efficient, since every time you erase an element of the vector, the elements that follow it have to be shifted back to fill in the empty space. So this will be slow if your objects vector is large. If you used a std::list instead (or if you are already using that), then this is not a problem, but lists have other drawbacks.
A secondary approach is to overwrite each pointer to a deleted object with nullptr and then use std::remove_if to remove them all in one go at the end of the loop. E.g.:
void Engine::update(string command) {
if (getGameOver() == false) {
for (p = objects.begin(); p != objects.end(); p++) {
// ...
killUpdate(command);
}
}
std::erase(std::remove_if(objects.begin(), objects.end(),
[](const Object* o) { return o == nullptr; }),
objects.end());
}
void Engine::killUpdate(std::string command) {
if ((*p)->getIsDead() == true) {
delete *p;
*p = nullptr;
}
}
The assumption this time is that you will never have a nullptr element of objects that you want to keep for some reason.
Since you seem to be a beginner, I should note that this:
std::erase(std::remove_if(objects.begin(), objects.end(),
[](const Object* o) { return o == nullptr; }),
objects.end());
is the erase-remove idiom, which is explained well on Wikipedia. It erases elements from the vector if they return true when a given function object is called on them. In this case, the function object is:
[](const Object* o) { return o == nullptr; }
Which is a lambda expression and is essentially shorthand for an instance of an object with this type:
class IsNull {
public:
bool operator() (const Object* o) const {
return o == nullptr;
}
};
One last caveat to the second approach, I just noticed that you have another loop over objects in scrollUpdate. If you choose the second approach, be sure to update this loop to check for nullptrs in objects and skip them.
Here is an issue (formatted for readability):
void Engine::update(string command)
{
if (getGameOver()==false)
{
for (p=objects.begin();p!=objects.end();p++)
{
spawnUpdate(command); // changes vector
//...
}
}
//...
}
void Engine::spawnUpdate(string command)
{
//...
objects.push_back(new Bullet((*p)->getPosX(),(*p)->getPosY(),(*p)->getState())); // no
//...
}
You have a loop with iterator p that points to elements in the object vector. When you call objects.push_back, the iterator for the vector may become invalidated. Thus that loop iterator p is no longer any good. Incrementing it in the for() will cause undefined behavior.
One way to get around this is to create a temporary vector that holds your updates. Then you add the updates at the end of your processing:
void Engine::update(string command)
{
std::vector<Object*> subVector;
if (getGameOver()==false)
{
for (p=objects.begin();p!=objects.end();p++)
{
spawnUpdate(command, subVector);
//...
}
}
// add elements to the vector
object.insert(object.end(), subVector.begin(), subVector.end());
}
void Engine::spawnUpdate(string command, std::vector<Object*>& subV)
{
if (command.compare("shoot")==0)
{
cout<<"Bang!"<<endl;
if ((*p)->getType() == PLAYER)
subV.push_back(new Bullet((*p)->getPosX(),(*p)->getPosY(),(*p)->getState()));
cout<<"Bullet success "<<endl;
}
}
You could avoid most of these issues by not using raw pointers. Clearly your code uses the semantic that the vector owns the pointers, so you can express this directly:
std::vector< std::unique_ptr<Object> > objects;
Then you may insert into the vector by using objects.emplace_back(arguments,to,Object,constructor); , and when you remove from the vector it will automatically delete the Object.
You still need to watch out for erase invalidating iterators, so keep using the erase-remove idiom as explained by Tyler McHenry. For example:
objects.erase( std::remove_if( begin(objects), end(objects),
[](auto&& o) { return o->getIsDead(); }), end(objects) );
Note - auto&& is permitted here since C++14; in C++11 you'd have to use std::unique_ptr<Object>&. Required includes are <algorithm> and <memory>.
And please stop using global iterators, keep p local to the function and pass any arguments you need to pass.
i got some issues trying to put the values of my vector in a new map (maMap)
If someone could explain me what contain my ItemIterator or how to do...
map<std::string,Employee*> Entreprise::convertiVectorMap() const
{
map<std::string,Employee*> maMap;
vector<Employee*>::const_iterator ItemIterator;
for(ItemIterator = vector_employe.begin(); ItemIterator != vector_employe.end(); ItemIterator++)
{
maMap.insert(std::pair<string,Employee*>(ItemIterator->getNom(),ItemIterator));
}
}
Your map is of <std::string, Employee*>, but you are trying to add an iterator as the second element of the pair. You need to dereference the iterator to get the Employee pointer.
maMap.insert(std::pair<string,Employee*>((*ItemIterator)->getNom(), *ItemIterator));
Or to save from dereferencing the same iterator twice, you could just use a range based for loop. As #CaptainObvlious mentions, you can also use std::make_pair to add to your map.
for(auto const employee: vector_employe)
{
maMap.insert(std::make_pair(employee->getNom(), employee));
}
You forgot to derefrence your iterator:
maMap.insert(std::pair<string,Employee*>((*ItemIterator)->getNom(),*ItemIterator));
And since everyone asks for a revamped version of your code here we go:
map<std::string,Employee*> Entreprise::convertiVectorMap() const
{
map<std::string,Employee*> maMap;
for(vector<Employee*>::const_iterator ItemIterator = vector_employe.cbegin(),
ItemIteratorEnd = vector_employe.cend();
ItmeIterator != ItemIteratorEnd; ++ItemIterator)
{
Employee* ptr = *ItemIterator;
maMap.insert(std::make_pair(ptr->getNom(),ptr));
}
}
You can also use ranged based for if you're at least in C++11.
I'm currently working on a big project and I need to use weak_ptr instead of shared_ptr.
Here is my problem.
I have a class named House with an attribute: vector<boost::shared_ptr<People>> my_people.
I want to modify this data member to be vector<boost::weak_ptr<People>> my_people.
My getter was
vector<boost::shared_ptr<People>>& getPeople() const
{
return my_people;
}
Normally, with a simple weak_ptr I can return my_people.lock();
But I have a vector and I don't know how to do something like this:
vector<boost::shared_ptr<People>>& getPeople() const
{
for( vector<boost::weak_ptr<People>::iterator it = my_people.begin();
it != my_people.end();
++it)
{
(*it).lock();
}
return my_people;
}
In other words, I want to return my vector of weak_ptr but as a vector of shared_ptr. Is it possible? Or do I have to return a vector of weak_ptr and use lock() everywhere I use them?
Your function is a reasonable start:
vector<boost::shared_ptr<People>>& getPeople() const
{
for( vector<boost::weak_ptr<People>::iterator it = my_people.begin();
it != my_people.end();
++it)
{
(*it).lock();
}
return my_people;
}
But calling (*it).lock() just creates a shared_ptr and throws it away, it doesn't change the type of the vector elements, and you can't return the vector as a different type.
You need to create a vector of the right type, fill it with the shared_ptr objects, and return it:
vector<boost::shared_ptr<People>> getPeople() const
{
vector<boost::shared_ptr<People>> people(my_people.size());
std::transform(my_people.begin(), my_people.end(), people.begin(),
boost::bind(&boost::weak_ptr<People>::lock, _1));
return people;
}
This iterates over each element of my_people, calls lock() on it, and assigns the result to the corresponding element of people.
If you know that my_people never contains expired pointers it's even easier:
vector<boost::shared_ptr<People>> getPeople() const
{
vector<boost::shared_ptr<People>> people(my_people.begin(), my_people.end());
return people;
}
This fills the people vector by constructing each shared_ptr element from a weak_ptr element. The difference is that this version will throw an exception if a weak_ptr has expired because the shared_ptr constructor throws if passed an expired weak_ptr. The version usingtransform will put an empty shared_ptr in the vector if an expired weak_ptr is transformed.
You could use std::transform
std::vector<std::shared_ptr<People>> temp;
sharedTargetList.resize(my_people.size());
//transform into a shared_ptr vector
std::transform(my_people.begin(),
my_people.end(),
temp.begin(),
[](std::weak_ptr<People> weakPtr){ return weakPtr.lock(); }
);
What about:
vector<boost::shared_ptr<People>> getPeople() const
{
vector<boost::shared_ptr<People>> res;
for( vector<boost::weak_ptr<People>::iterator it = my_people.begin();
it != my_people.end(); ++it)
res.push_back(it->lock());
return res;
}
Also, you can filter out the null pointers, if you want.
Of course, you cannot return a reference to a local variable so you have to return a copy. You may want to do instead:
void getPeople(vector<boost::shared_ptr<People>> &res) const
{
for( vector<boost::weak_ptr<People>::iterator it = my_people.begin();
it != my_people.end(); ++it)
res.push_back(it->lock());
}
to avoid copying the return vector.
Note that vector<weak_ptr<T> > and vector<shared_ptr<T> > are two completely different types.
However, you can write a function that accepts the former and returns the latter:
template<class Ptrs, class WeakPtrs>
void lockWeakPtrs(const WeakPtrs &weakPtrs, Ptrs &ptrs)
{
BOOST_FOREACH (typename WeakPtrs::const_reference weakPtr, weakPtrs)
{
typename Ptrs::value_type ptr = weakPtr.lock();
if (ptr) // if you want to drop expired weak_ptr's
ptrs.insert(ptrs.end(), ptr);
}
}
Call like this:
lockWeakPtrs(myWeakVector, mySharedVector);
I guess I don't fully understand how destructors work in C++. Here is the sample program I wrote to recreate the issue:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Odp
{
int id;
Odp(int id)
{
this->id = id;
}
~Odp()
{
cout << "Destructing Odp " << id << endl;
}
};
typedef vector<shared_ptr<Odp>> OdpVec;
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
shpoutOdp.reset();
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp.reset(iter->get());
return true;
}
}
return false;
}
int main()
{
OdpVec vec;
vec.push_back(shared_ptr<Odp>(new Odp(0)));
vec.push_back(shared_ptr<Odp>(new Odp(1)));
vec.push_back(shared_ptr<Odp>(new Odp(2)));
shared_ptr<Odp> shOdp;
bool found = findOdpWithID(0, shOdp, vec);
found = findOdpWithID(1, shOdp, vec);
}
Just before main() concludes, the output of this program is:
Destructing Odp 0
Destructing Odp 1
Why does this happen? I'm retaining a reference to each of the Odp instances within the vector. Does it have something to do with passing a shared_ptr by reference?
UPDATE I thought that shared_ptr::reset decremented the ref count, based on MSDN:
The operators all decrement the
reference count for the resource
currently owned by *this
but perhaps I'm misunderstanding it?
UPDATE 2: Looks like this version of findOdpWithID() doesn't cause the destructor to be called:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
{
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
{
Odp& odp = *(iter->get());
if (odp.id == id)
{
shpoutOdp = *iter;
return true;
}
}
return false;
}
This line right here is probably what is tripping you up.
shpoutOdp.reset(iter->get());
What you're doing here is getting (through get()) the naked pointer from the smart pointer, which won't have any reference tracking information on it, then telling shpoutOdp to reset itself to point at the naked pointer. When shpoutOdp gets destructed, it's not aware that there is another shared_ptr that points to the same thing, and shpoutOdp proceeds to destroy the thing it's pointed to.
You should just do
shpoutOdp = *iter;
which will maintain the reference count properly. As an aside, reset() does decrement the reference counter (and only destroys if the count hits 0).
So many things that are being used nearly correctly:
bool findOdpWithID(int id, shared_ptr<Odp> shpoutOdp, OdpVec& vec)
Here the parameter shpoutOdp is a a copy of the input parameter. Not such a big deal considering it is a shared pointer but that is probably not what you were intending. You probably wanted to pass by reference otherwise why pass it to the function in the first place.
shpoutOdp.reset();
Resetting a parameter as it is passed in.
Does this mean it could be dirty (then why have it as an input parameter) it make the function return a shared pointer as a result if you want to pass something out.
Odp& odp = *(iter->get());
Don't use get on shared pointers unless you really need to (and you really if ever need too). Extracting the pointer is not necessary to get at what the pointer points at and makes you more likely to make mistakes because you are handling pointers. The equivalent safe(r) line is:
Odp& odp = *(*iter); // The first * gets a reference to the shared pointer.
// The second star gets a reference to what the shared
//pointer is pointing at
This is where it all goes wrong:
shpoutOdp.reset(iter->get());
You are creating a new shared pointer from a pointer. Unfortunately the pointer is already being managed by another shared pointer. So now you have two shared pointers that think they own the pointer and are going to delete it when they go out of scope (the first one goes out of scope at the end of the function as it is a copy of the input parameter (rather than a reference)). The correct thing to do is just to do an assignment. Then the shared pointers know they are sharing a pointer:
shpoutOdp = *iter; // * converts the iterator into a shared pointer reference
The next line though not totally wrong does assume that the iterators used are random access (which is true for vector).
for (OdpVec::iterator iter = vec.begin(); iter < vec.end(); iter++)
But this makes the code more brittle as a simple change in the typedef OdpVec will break the code without any warning. So to make this more consistent with normal iterator usage, use != when checking against end() and also prefer the pre increment operator:
for (OdpVec::iterator iter = vec.begin(); iter != vec.end(); ++iter)
shared_ptr::reset destroys the contents already in the shared_ptr. If you want to affect only that single shared_ptr reference, simply assign to it.
EDIT: In response to comment, you can fix it by changing the body of your for loop to:
if ((*iter)->id == id)
{
shpoutOdp = *iter;
return true;
}
EDIT2: That all said, why aren't you using std::find_if here?
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm> //for std::find_if
#include <functional> //for std::bind
struct Odp
{
int id;
int GetId()
{
return id;
}
Odp(int id)
{
this->id = id;
}
~Odp()
{
std::cout << "Destructing Odp " << id << std::endl;
}
};
typedef std::vector<shared_ptr<Odp> > OdpVec;
int main()
{
OdpVec vec;
vec.push_back(std::shared_ptr<Odp>(new Odp(0)));
vec.push_back(std::shared_ptr<Odp>(new Odp(1)));
vec.push_back(std::shared_ptr<Odp>(new Odp(2)));
OdpVec::iterator foundOdp = std::find_if(vec.begin(), vec.end(),
std::bind(std::equal_to<int>(), 0, std::bind(&Odp::GetId,_1)));
bool found = foundOdp != vec.end();
}
The nice thing about shared_ptr is that it handles the ref-counting internally. You don't need to manually increment or decrement it ever. (And that is why shared_ptr doesn't allow you to do so either)
When you call reset, it simply sets the current shared_ptr to point to another object (or null). That means that there is now one less reference to the object it pointed to before the reset, so in that sense, the ref counter has been decremented. But it is not a function you should call to decrement the ref counter.
You don't ever need to do that. Just let the shared_ptr go out of scope, and it takes care of decrementing the reference count.
It's an example of RAII in action.
The resource you need to manage (in this case the object pointed to by the shared_ptr) is bound to a stack-allocated object (the shared_ptr itself), so that its lifetime is managed automatically. The shared_ptr's destructor ensures that the pointed-to object is released when appropriate.