I have a vector contains a,b,c,d,e
vec[2] is c, but will it automatically reorder after i delete/erase c ? i mean vec[2] is d after the operation.
Logically yes, as a vector is a dynamic array of element. You delete one, then everything that follows is moved.
In the same manner, the total length of the vector will decrease as you erase elements.
From cplusplus.com
This effectively reduces the vector size by the number of elements
removed, calling each element's destructor before.
Because vectors keep an array format, erasing on positions other than
the vector end also moves all the elements after the segment erased to
their new positions, which may not be a method as efficient as erasing
in other kinds of sequence containers (deque, list).
According to the standard:
iterator erase(const_iterator position);
....
Effects: Invalidate iterators and references at or after the point of the erase
Complexity: The destructor of T is called the number of times equal to the number of the elements erased, but the move assignment operator of T is called the number of times equal to the number of elements in the vector after the erased elements.
As you see the move assignment operator will be called as many times as there are elements after the erased element, and every reference/iterator to the elements after are invalidated.
So when one element is erased, all elements following are moved to fill in the "blank" space where the erased element was.
The point about the invalidated references/iterators are very important to remember, esp. if you are erasing in a loop. According to the newest standard erase should return you an iterator to the next element which you could use, or the erase-and-remove idiom
If you are worried about the side effects of this during an iteration, use the Erase-remove idiom
Related
As insert an element in vector will relocate the afterward elements, as below said:
Because vectors use an array as their underlying storage, inserting elements in positions other than the vector end causes the container to relocate all the elements that were after position to their new positions. This is generally an inefficient operation compared to the one performed for the same operation by other kinds of sequence containers (such as list or forward_list).
I want to know, will it relocate elements of other lists in the vector, if I insert or erase the elements of one list in std::vector<std::list<int>>. I concern the efficiency of this kind of insert and erase operation. Is the complexity of this kind of insert and erase operation still be constant as normal insert and erase operation in std::list<int>?
If you're referring to inserting an element in the list, it would be constant time and not affect other elements in the vector. Why would it? If you're inserting a list into the vector, then that would move the elements behind it and take linear time.
#include <vector>
#include <list>
int main()
{
std::vector<std::list<int>> foo(3, std::list<int>({2, 3, 4}));
foo.emplace(foo.begin(), std::list<int>({7, 8})); //shifts all lists behind insertion point
foo[0].insert(foo[0].begin(), 42); //const time, has nothing to do with other lists
}
Note that even though the lists are shifted, the elements stored inside likely won't be shifted since the lists only stores pointers to them. This allows a list to be moved in constant time, therefore the time it takes inserting a list into a vector isn't affected by the number of elements in the lists.
Which operation is most costly in C++?
1. Resize of a vector (decrease size by 1)
2. Remove last element in vector
From http://en.cppreference.com/w/cpp/container/vector which essentially quotes the Standard:
void pop_back();
Removes the last element of the container.
No iterators or references except for back() and end() are invalidated.
void resize( size_type count );
Resizes the container to contain count elements. If the current size
is greater than count, the container is reduced to its first count
elements as if by repeatedly calling pop_back().
So in this case, calling resize(size() - 1) should be equivalent to calling pop_back(). However, calling pop_back() is the right thing to do as it expresses your intent.
NOTE: the answer is reflecting the changed interface of C++11's std::vector::resize(), which used to contain a hidden default argument which was being copied around (and which may or may not have been optimized away).
In my opinion they are equivalent. The both operations remove the last element and decrease the size.:)
According to the C++ Standard
void resize(size_type sz); 12 Effects: If sz <= size(), equivalent to
calling pop_back() size() - sz times
So they are simply equivalent according to my opinion and the point of view of the Standard.:)
Also if to consider member function erase instead of pop_back (in fact they do the same in this case) then according to the same Standard
4 Complexity: The destructor of T is called the number of times equal
to the number of the elements erased, but the move assignment
operator of T is called the number of times equal to the number of
elements in the vector after the erased elements.
As there are no move operations for the last element then the cost is the same.
I have a vector V and I would like to store which elements of this vector I will later have to remove.
To do that I've used an other vector Y to store the iterators of the elements of V that I want to remove. So I iterate through Y to access the iterators of the elements I need to remove in V.
The problem is that when you erase elements from V, all the iterators in Y (pointing on elements of V) become invalid.
I can't find any answer but it seems so trivial that there must be a simple workaround, isn't it ?
Use V.erase(std::remove_if(V.begin(), V.end(), MyPredicate()), V.end())
You can use std::vector<unsigned> indices to store the index values of each element.
Iterators, pointers and references pointing to position (or first) and
beyond are invalidated, with all iterators, pointers and references to
elements before position (or first) are guaranteed to keep referring
to the same elements they were referring to before the call.
http://www.cplusplus.com/reference/vector/vector/erase/
So if the elements (iterators) of Y are sorted, you could just iterate over Y backwards and delete the corresponding elements in V. This works since only iterators to later elements in V are invalidated when you erase elements in V.
This is about complexity.
You can erase elements from higher indices to lower indices, in that case your approach will work, but each time you erase an element of a vector, the ones following it will be relocated. This has complexity linear in the number of element following the erased element, so I would expect sort of quadratic compexity, or O(number_of_elements * number_of_elements_to_be_erased).
If the number of deletions is high, and the indices of elements to erase are distributed more or less uniformly over the entire range, a better solution from the complexity point of view would be to work on the complement of "element to be erased". Instead, it would be "elements to be retained", you would copy the elements which should stay to a new array and assign it to the old one. This is linear in the number of elements in the vector, O(number_of_elements).
Better still, if you have a complete control over the implementation and can change std::vector for std::list, you could proceed with the rest exactly as you described, with no ill side effects. In that case, the erases are also efficient, done in constant time, so the entire operation is O(number_of_elements_to_be_erased).
I understand that having pointers to elements of a vector is a bad idea, because upon expanding, the memory addresses involved will change, therefore invalidating the pointer(s). However, what if I were to simply use an integer that holds the index number of the element I want to access? Would that be invalidated as the vector grows in size? What I am thinking of looks something like this:
#include <vector>
class someClass{
string name
public: string getName(){return name;}
};
vector<someClass> vObj;
int currIdx;
string search;
cout<<"Enter name: ";
cin>>search;
for(int i=0; i<vObj.size(); i++){
if(vObj[i].getName()==search)
currIdx = i;}
No, the index numbers are of course not invalidated when the vector expands. They are invalidated (in the sense that you no longer find the same elements at a constant index) if you erase a previous element, though:
vector: 3 5 1 6 7 4
Here, vector[2] == 1. But if you erase vector[1] (the 5), then afterwards, vector[2] == 6.
I think the title of your question and what you seem to be asking do not really match. No vector is by definition guaranteed to be sorted, so elements won't be "in order".
Moreover, all iterators and references to elements of a vector will be invalidated upon insertion only if reallocation occurs (i.e. when the size of the vector exceeds its capacity). Otherwise, iterators and references before the point of insertion will not be invalidated (see Paragraph 23.3.6.5/1 of the C++11 Standard).
Storing an index is only subject to a potential logical invalidation: if you insert elements into the vector in a position prior to the one you are indexing, the element you were indexing will be shifted one position to the right, and the same index will now refer to a different element ; likewise, if you erase an element prior to the position you were indexing, the element you were indexing will be shifted on position to the left - and your index may now refer to a position which is out-of-bounds.
No, the index numbers are not invalidated when the vector is expanded. Since you're declaring that the vector container object is not a pointer vector<someClass> instead of vector<someClass*>, your pointed to element will be preserved as well.
It shouldn't, as the system will simply allocate more memory and then do a memcopy.
Order should be preserved in the std::vector STL template.
And yes, if you delete elements the ordering will change. But if you are going to be doing a lot of deletes, use a different data structure such as a linked list.
According to Stroustrup : The C++ programming language :-
"When a vector is resized to accommodate more (or fewer) elements, all of its elements may be
moved to new locations."
Is this holds true, even if the vector is re-sized to smaller size ?
Case 1: If the new size being requested is greater than the current std::vector::capacity() then all elements will be relocated.
Case 2: If the new size being requested is lesser than the current std::vector::capacity() then there will be no relocation of elements.
Standerdese Evidence:
The standard defines effect of vector::resize() as:
C++11 Standard 23.3.6.3/12 vector capacity:
void resize(size_type sz, const T& c);
Effect:
if (sz > size())
insert(end(), sz-size(), c);
else if (sz < size())
erase(begin()+sz, end());
else
; // do nothing
As #DavidRodrÃguez-dribeas correctly points out, Iterator invalidation rules for std::vector::insert() operation are:
23.3.6.5 vector modifiers
1 [insert,push_back,emplace,emplace_back]
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
Essentially this means:
All iterators and references before the point of insertion will be unaffected, unless the new container size is greater than the previous capacity because in such a scenario all elements might be moved to new locations thus invalidating pointers/iterators to original location.Since resize() only erases/inserts elements at the end of the container[Note 1].The governing factor boils down to size being requested as against current capacity.
Hence the Case 1 result.
In Case 2 std::vector::erase() will be called and the invalidation rule in this case is:
23.3.6.5 vector modifiers
iterator erase(const_iterator position);
3 Effects: Invalidates iterators and references at or after the point of the erase.
Since [Note 1], elements will be only removed at end and there is no need of relocation of all elements.
...elements may be moved to new locations."
Notice how it says may be moved. So that would imply that it depends what what kind of a resize it is.
Iterators in a vector are invalidated for two reasons. An element is inserted/removed before the location of the iterator (1) or the whole buffer is relocated (2) if the vector needs to grow it's capacity. The key here is a change to the capacity().
Because resize() only inserts/removes from the end of the container. When the vector shrinks only those iterators referring to the elements being removed become invalidated. When the vector grows no iterator will become invalid if the new size is smaller than capacity(), and all iterators will be invalidated if the new size is larger.
Since Als provided incorrect evidence1, I am adding here the correct quotes:
23.3.6.5 vector modifiers
1 [insert,push_back,emplace,emplace_back]
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
2 [erase]
Effects: Invalidates iterators and references at or after the point of the erase.
Similar quotes can be found in C++03.
1 Avoiding to duplicate the quote that dictates the equivalence of resize to either insert or erase. Which is right.
The answer in the body of the question ""When a vector is resized to accommodate more (or fewer) elements, all of its elements may be moved to new locations.""