What happens to QVector's item when it's deleted elsewhere? - qt

I'm wondering about what happens when I delete a QVector's item?
Is it automatically removed from the
QVector?
Do I have to remove it manually?
Also, how can I find out the index of an iteration of the iterator?
Best regards

If you have a QVector<Thing*> and delete one of the Things that stored in it, it will not be removed automatically from the vector. You need to do that yourself.
As far as I know, and from what I read in the docs, none of the QVector iterators has a method to tell at what index it is positioned.
But if you have a reference to the vector itself (or at least to it's begin() iterator), you can use:
int position = iter - v.begin();

Related

optimization: vector.erase() of pointer

I have a question about deleting a dynamic vector of pointers and optimization.
Here is my code. It checks wether an element has to be set to nullptr and then it delete all those elements.
for (auto* el : elements)
{
if (el != 0)
// do something
else
el = nullptr;
}
elements.erase(std::remove(elements.begin(), elements.end(), nullptr), elements.end());
Is the complexity of this operation onerous for the machine ?
And if it is, then is there a better way of doing it and it is worth it ? Because, here, the preservation of the index order is not important for me.
Thank you !
Is the complexity of this operation onerous for the machine ?
It is a bit costly, but not much more than the previous operation. Indeed, remove will typically check the value of each item, and if an item needs to be removed, the algorithm shifts the item on the right to put it on the current analysed item. erase is often relatively cheap since it just resizes the vector to skip the remaining garbage at the end (generally without any copy or reallocation) and call the destructor of the discarded items (costly only if there is a lot of them and the destructor is non-trivial). This operation can be as costly as the previous one.
And if it is, then is there a better way of doing it and it is worth it ? Because, here, the preservation of the index order is not important for me.
Yes, this is possible: you can just iterate over the array with a classical loop and swap the current item with the one of the end to discard it. You need to maintain a end iterator moving from the end to the beginning. The loop stops when the end iterator is reached. Note that the swapped items coming from the end should be checked by your predicate too.
Alternatively, you could just use std::partition at this algorithm does a quite similar job and is simpler: it puts the items validating a given condition to the left part and put the other on the right part. You can then just resize the array to remove the unwanted right part.
std::partition should is bit less efficient than the other swap-based approach only if there is a lot of item to remove since it has to maintain the consistency of both sides.
Here is an (untested) example with std::partition:
auto discardedBegin = partition(elements.begin(), elements.end(), doSomething);
elements.erase(discardedBegin, elements.end());

Error with QMutableListIterator

After appending an item to a QList which is pointed to by a QMutableListIterator, I find out the next value of the iterator points outside the list.
Or can't I point the iterator to any where in the list except the beginning or end?
Please I need help.
From the Qt documentation:
no changes should be done directly to the list while the iterator is active (as opposed to through the iterator), since this could invalidate the iterator and lead to undefined behavior.
Appending to the list might cause it's memory to be reallocated, which would mean that the iterator points to an invalid location.

QT elements in vector initialization. How to solve private copy constructor problem?

I wanted to create a vector of a subclass of QGraphicsRectItem, named MyRect. This vector is initialized in MyClass:
MyClass::MyClass () : myVector_(80, std::vector<MyRect>(60, MyRect(true,true)))
...
I learned that vector constructs the first element and then copies it with the copy constructor. The problem is that QGraphicsRectItem's copy constructor is private and this doesn't work. (Very long error message, one hour of googling)
Now I have three possible solutions as I see it:
1.)Make a for-loop and populate myVector myself in the constructor body.
1b.) Just use regular array because it remains static anyway.
2.)Use MyRect* instead of MyRect as content of myVector (manual memory allocation -> bad)
3.)Use QVector that uses Object* by default and manages the memory for me.
After spending at least one hour on solving this I would like to hear from you if there are other good possibilities or what you think is the best solution. I am on the verge of dropping vectors for this and just using arrays.
The vector, as you declared it, will have to manipulate instances of MyRect. This means that depending of what you do the with the elements of the vector, or if you copy the vector, the MyRect instances might be duplicated.
This is not possible, because that would mean creating a new item each time a copy occurs (this is why the QGraphicsItem constructor is private). You have to manipulate the items of your scene through a pointer.
Thus, to me the best solution is to store in your vector pointers on your items (your 2nd solution) :
std::vector<MyRect*>
Memory management shouldn't be a problem at all, as this will be handled by Qt : when you destroy the scene, all items part of this scene will be destroyed.
Your vector won't duplicate items (no instanciation), only pointers, which means you won't create new items you'd have to destroy yourself.

Deleting items in foreach

Should you be allowed to delete an item from the collection you are currently iterating in a foreach loop?
If so, what should be the correct behavior?
I can take quite a sophisticated Collection to support enumerators that track changes to the collection to keep position info correct. Even if it does some compromisation or assumptions need to be made. For that reason most libraries simply outlaw such a thing or mutter about unexpected behaviour in their docs.
Hence the safest approach is to loop. Collect references to things that need deleting and subsequently use the collected references to delete items from the original collection.
It really depends on the language. Some just hammer through an array and explode when you change that array. Some use arrays and don't explode. Some call iterators (which are wholly more robust) and carry on just fine.
Generally, modifying a collection in a foreach loop is a bad idea, because your intention is unknown to the program. Did you mean to loop through all items before the change, or do you want it to just go with the new configuration? What about the items that have already been looped through?
Instead, if you want to modify the collection, either make a predefined list of items to loop through, or use indexed looping.
Some collections such as hash tables and dictionaries have no notion of "position" and the order of iteration is generally not guaranteed. Therefore it would be quite difficult to allow deletion of items while iterating.
You have to understand the concept of the foreach first, and actually it depends on the programming language. But as a general answer you should avoid changing your collections inside foreach
Just use a standard for loop, iterate through the item collection backwards and you should have no problem deleting items as you go.
iterate in reverse direction and delete item one by one... That should proper solution.
No, you should not. The correct behaviour should be to signal that a potential concurrency problem has been encountered, however that is done in your language of choice (throw exception, return error code, raise() a signal).
If you modify a data structure while iterating over its elements, the iterator might no longer be valid, which means that you risk working on objects that are no longer part of the collection. If you want to filter elements based on some more complex notation, you could do something like this (in Java):
List<T> toFilter = ...;
List<T> shadow;
for ( T element : toFilter )
if ( keep(element) )
shadow.add(element);
/* If you'll work with toFilter in the same context as the filter */
toFilter = shadow;
/* Alternatively, if you want to modify toFilter in place, for instance if it's
* been given as a method parameter
*/
toFilter.clear();
toFilter.addAll(shadow);
The best way to remove an item from a collection you are iterating over it to use the iterator explitly. For example.
List<String> myList = ArrayList<String>();
Iterator<String> myIt = myList.iterator();
while (myIt.hasNext()) {
myIt.remove();
}

Removing rows from QTreeWidget (qt programming)

what's the best way to remove a row (QTreeWidgetItem) from a QTreeWidget?
The QTreeWidget content has been set by:
myQTreeWidget->insertTopLevelItems(0, items); // items = QList<QTreeWidgetItem*>
then I remove an item from my QList "items" and I try to clear/reset the QTreeWidget
packList->clear();
packList->insertTopLevelItems(0, items);
but my app crashes here!
Suggestions?
Your problem is that calling packList->clear() deletes the tree widget items contained by the tree. (See the documentation about QTreeWidget::clear(), which includes a note about the items being removed from the tree before deleting.) You'll either need to find a way to remove the items, or not maintain a list of them separately from the tree.
On a slightly-related note, if you are trying to keep track of other data along with the tree, I'd recommend you try to use the models paradigm. In non-trivial cases, it has usually been worth my while to convert to that technique, rather than using the widgets/items.
From what this documentation says, you should be able to do it with:
packList->takeTopLevelItem(index);
Which returns removes and returns the item at the supplied index.

Resources