How to modify objects using an iterator? using <list> - c++

So here's an example. The star's mLocation and mSpeed are a Vector3 custom type.
I'v tried:
Star &star = *iStar;
Star star = *iStar;
Using iStar-> directly doesn't work with my operators, not sure why.
So whats the proper way to do that?
void UniverseManager::ApplySpeedVector()
{
std::list <Star>::const_iterator iStar;
for (iStar = mStars.begin(); iStar != mStars.end(); ++iStar)
{
// how to I get a hold on the object the iterator is pointing to so I can modify its values
// i tried Star &star = *iStar; this is illegal
// tried just using the iStar->mLocation += iStar->mSpeed this also fails due to the operator not accepting the values not sure why
// tried other things as well, so what is the proper way to do this?
iStar->SetLocationData( iStar->mLocation += iStar->mSpeed);
}
}

std::list<Star>::const_iterator iStar;
You cannot modify the objects in a container via a const_iterator. If you want to modify the objects, you need to use an iterator (i.e., std::list<Star>::iterator).

As James told you, you should use a std::list<Star>::iterator so that you can modify the object by calling a method or accessing its member variables.
It would be something like this:
void UniverseManager::ApplySpeedVector()
{
std::list <Star>::iterator iStar;
for (iStar = mStars.begin(); iStar != mStars.end(); ++iStar)
{
iStar->SetLocationData(iStar->mLocation += iStar->mSpeed);
}
}
Nevertheless, if you want to improve your code you might prefer to have a getter for accessing the location and the speed:
void UniverseManager::ApplySpeedVector()
{
std::list <Star>::iterator iStar;
for (iStar = mStars.begin(); iStar != mStars.end(); ++iStar)
{
iStar->SetLocationData(iStar->GetLocationData() + iStar->GetSpeed());
}
}
In any case, you have to use a non-const iterator.

Related

Deleting and Reassigning a pointer from QList<Object *> to new Object using Iterator, is this right?

I have a QList and I'm trying to replace the objects in the list with new objects. The context is that I have a list of custom objects (the class name is "Conversation") to represent a list of group chats in a messaging platform. I use std::find_if to iterate through the list of pointers to find one with the right ID, and I want to take the pointer to that found object, deallocate it (delete?), and reassign that pointer to point at an object I generate with the "new" keyword. I think I'm doing this right but I'm not sure how to verify.
I tried a couple different iterations, ran into some issues where I realized I was using a const_iterator rather than just an iterator, so I couldn't modify any data. But I've fixed that and it seems like it's working, but I'm not positive.
Here's what I've got:
GroupChat *gc = new GroupChat(); // extends Conversation
// ...I update the member data here...
auto foundChat = std::find_if(conversations_.Conversations.begin2(),
conversations_.Conversations.end2(),
[this, gc](Conversation* o) { // my code to find the correct one...
}
if (foundChat != conversations_.Conversations.end()) {
auto c = (*foundChat);
delete c; // Is this right? Not positive...
//*foundChat = nullptr; // do I need this?
c = gc;
}
It seems like it's working but I'm worried about dangling pointers and incorrect memory deallocation/allocation. Could someone spot check me on this? Thanks for any help!

Why I cannot use Java-Style QMapIterator in QMap.erase()?

I'm trying this code in Qt 5.12:
//Definition of map
QMap<int, ZoneData*> mapId2Pointer_;
//Some values filled in mapId2Pointer_ here...
QMapIterator<int, ZoneData*> iteratorId2Pointer(mapId2Pointer_);
while (iteratorId2Pointer.hasNext())
{
iteratorId2Pointer.next();
if (iteratorId2Pointer.value() == _climbZoneData)
{
iteratorId2Pointer = mapId2Pointer_.erase(iteratorId2Pointer); //<- ERROR
}
}
But, in the last line of code compiler gives this error:
error: no matching function for call to 'QMap::erase(QMapIterator&)'
iteratorId2Pointer = mapId2Pointer_.erase(iteratorId2Pointer);
I've seen a lot of examples and googled a lot, with no success. All examples look similar to the above one, every one uses a QMapIterator as parameter in QMap::erase() call. But no conversion from QMapIterator to QMap::iterator seems to be possible in my code.
What can be the reason of this error?
QMapIterator is a "Java-style iterator" and QMap::iterator is a "STL-style iterator". They are not compatible; you need to use one or the other.
You can iterate using a QMap::iterator like this:
QMap<int, ZoneData*>::iterator i = mapId2Pointer_.begin();
while(i != mapId2Pointer_.end()) {
if (i.value() == _climbZoneData) {
i = mapId2Pointer_.erase(i);
} else {
++i;
}
}
Note that if you weren't erasing things in the middle (which invalidates your iterator, requiring you to set it to the return value from erase), you could use a for loop instead.

C++: Convenient pointer array based value iterator for super-heavy objects

In my code, I have something like this:
vector<SuperHeavyObject> objects; // objects in this vector are extremely slow to copy!
for (auto &objectGroup : objectGroups) {
vector<SuperHeavyObject> objectsInThisGroup;
for (size_t index : objectGroup) {
objectsInThisGroup.push_back(objects[index]); // slow as copying is needed!
}
doSomething(objectsInThisGroup.begin(), objectsInThisGroup.end());
}
What I'd really want is something like this:
vector<SuperHeavyObject> objects; // objects in this vector are extremely slow to copy!
for (auto &objectGroup : objectGroups) {
vector<SuperHeavyObject*> objectsInThisGroup; // pointers!
for (size_t index : objectGroup) {
objectsInThisGroup.push_back(&objects[index]); // not slow anymore
}
doSomething(magicIterator(objectsInThisGroup.begin()),
magicIterator(objectsInThisGroup.end()));
}
doSomething is allowed to copy the objects, so there's no scope problem. Inside doSomething is the only place where I'd like copying to take place, because these objects really are very slow to copy (I've profiled and it's a chokepoint).
At the same time, I don't want to change the signature of doSomething to accept iterators that dereference SuperHeavyObject*, because that would require a lot of changes; dereferencing to SuperHeavyObject would be ideal, as it would only happen at one place (where copying happens).
My question is; I could write an iterator like this myself, but it feels like I'm reinventing the wheel. Does C++ (11) have facilities to do this? I also have Boost if someone knows of anything like this.
Seems like a legitimate use case for std::reference_wrapper1:
vector<SuperHeavyObject> objects;
for (auto &objectGroup : objectGroups) {
vector<std::reference_wrapper<SuperHeavyObject>> objectsInThisGroup;
for (size_t index : objectGroup) {
// fast, we are only storing reference-like objects
objectsInThisGroup.push_back(objects[index]);
}
doSomething(objectsInThisGroup.begin(), objectsInThisGroup.end());
}
C++11 required
Thanks #matteo-italia for your helpful answer! I used it for a while, and decided to look closer at Boost's iterators, and I found that they have an indirect_iterator which is also a good way to do what I want.
"indirect_iterator adapts an iterator by applying an extra dereference inside of operator*()"
http://www.boost.org/doc/libs/1_59_0/libs/iterator/doc/indirect_iterator.html

c++ Iterate through a list to call a certain function

E.g. a class Unit has three functions:
class Unit{
void StandUp();
void SitDown();
void Die();
}
I have a list of pointers list<Unit*> UnitList;
When I want everyone to stand up:
void EveryoneStandUp(){
for(list<Unit*> it = UnitList.begin(); it != UnitList.eng(); it++){
(*it)->StandUp();
}
}
Now if I want everyone to SitDown, I would copy the code above and change StandUp() to SitDown(). For every new function I write, if I want everyone to do it, I have to have another for-loop body in my code.
Is it possible to put this for-loop body in another function, which I can reuse whenever I want to call a certain function from all of the members in the UnitList?
I feel like this must have answers somewhere else, I tried googling but have little idea which keywords I should look for. Thanks for answers!
You may do:
void Everyone(void (Unit::*method)())
{
for (std::list<Unit*>::iterator it = UnitList.begin(); it != UnitList.end(); it++){
((*it)->*method)();
}
}
And call it
Everyone(&Unit::StandUp);
but in c++11, your example may be rewritten as:
for (auto* unit : UnitList) {
unit->StandUp();
}
which seems clear enough.
you can use c++ algorithms available,
http://www.cplusplus.com/reference/algorithm/for_each/
It can be solved by having a helper function, which does the actual looping and have the member function to be called as an argument.
Something like this:
void UnitHelperFunction(void (Unit::*func)())
{
for (...)
{
((*itr)->*func)();
}
}
void EveryoneStandUp()
{
UnitHelperFunction(&Unit::StandUp);
}

Filling list inside object and accessing to it later

I'm sorry if the title isn't very explicit, but I'll try to explain it better. I'm not very familiar with c++ and I'm using openFrameworks for the first time. I'm trying to do something that's probably quite easy, at least in other languages it is, but I'm not being able to do it :(
I have a class Video and inside it I have an object list<ofImage> keyFrames; and several methods to interact with it like the following:
void addKeyFrame(ofImage img) {
if(keyFrames.size() == 0) {
keyFrames.push_front(img);
}
else {
keyFrames.push_back(img);
}
}
list<ofImage> * getKeyFrames() {
list<ofImage> *list = &keyFrames;
return list;
}
void clearKeyFrames() {
keyFrames.clear();
}
In other class I have several Video objects and I have a function that uses addKeyFrame(ofImage img) to fill the list for each object. In the end of that function if I print the list size it is greater than zero.
Inside draw() function I iterate each Video object and I try to draw each image inside their keyFrame list, but the list is always empty and I just filled it with images... I'm using getKeyFrames() function to return a pointer to the list. How can it be empty if I just added objects to it in another function and if I verified that the size was greater than zero? And if I try to debug the application I feel even more lost lol.
Please tell me if you need anymore information and if you know what I'm doing wrong. Thanks!
Ok, A few little things:
1- You shouldn't check for empty lists (or any other STL containers) like this:
if(keyFrames.size() == 0)
This is faster and more "stylish":
if(keyFrames.empty())
2- You've created an unnecessary variable here:
list<ofImage> * getKeyFrames() {
list<ofImage> *list = &keyFrames;
return list;
}
You could do just:
list<ofImage> * getKeyFrames() {
return &keyFrames;
}
3- Pointers are not (most times) the best solution in C++. A reference is the most used substitute, but it would be even better in htis case if you returned an iterator:
list<ofImage>::iterator GetBeginIterator() {
return keyFrames.begin();
}
This way you could use the iterator just like a pointer, increasing it to iterate trough the frames and dereferencing it (with * operator)...