I am using
std::vector<std::vector<int>> S; but the problem can apply to any additional dimension of nesting as well.
To clear content from S, is it sufficient from an efficiency/memory point of view to call S.clear(); or should one first clear the stored container, in this case std::vector<int> and then S.clear(); as:
std::vector<std::vector<int>>::iterator iter;
for (iter = S.begin(); iter != S.end(); iter++) {
iter->clear();
}
S.clear();
Calling clear() on the nested vector(s) is unneeded. When you call clear() on the outermost vector it will destroy each element in the vector. This will in turn destroy any nested vectors.
std::vector is an RAII type, so as long as you are storing an RAII type in it, it will do the right thing, since the types clean up after themselves.
Just call clear on the outermost container. The destructor of the contained containers will clean up their space automatically. The only thing to watch out for is if somewhere down the nesting you have owning raw-pointers. If you have code that does that just change it to use any kind of smart owning pointer, owning the object by value, or another level of container nesting instead of a raw pointer.
To be sure you really release the memory, you may want to do something like:
std::vector<std::vector<int>>().swap(S);
Related
I know that push_back() on an std::vector can cause reallocation and therefore invalidate iterators in the pointer. Is there a way of installing a hook on reallocations (which presumably happen very seldom) so that I can adjust iterators appropriately?
Ideally something like this:
class hook; // forward
std::vectorwithhook<T,hook> v;
auto pointer = v.end();
template<> class hook<T> {
void operator()(T *old, T *new) { pointer += new-old; }
}
and then I can push_back() on v and play with pointer with no fear.
IMHO the easiest way to do this would be to have your vectorwithhook::push_back return the new end() and use it like:
pointer = v.push_back(new_item);
NOTE: you would have to do this for all members that change content of the vector (e.g. emplace_back, pop_back, insert etc...)
Alternatively, it should also be possible by creating your own allocator type, which will take a reference to iterator and the container in constructor and update it every time allocator::allocate(...) or allocator::dellocate(...) is called. NOTE that this goes against the principals of STL that was designed to have iterators, containers, allocators separate from one another...
P.S. none of this sounds like a good idea tbh, I would think about reworking the code to avoid keeping the end() iterator instead of doing any of this.
Could you suggest safety deleting of element of std::vector in to cases:
1. Clear all elements of the vector;
2. Erase one element depending on condition.
What are the dangers of this code:
typename std::vector<T*>::iterator it;
for (it=std::vector<T*>::begin();it!=std::vector<T*>::end();it++)
{
if (*it) delete *it;
}
Thank's.
You don't remove the element from the vector. So the vector element is pointing to the location as before, i.e. the same T. However, since you have deleted that T you can not dereference the pointer anymore - that would be UB and may crash your program.
delete is calling the destructor of T (great, it's something you shall do) but deleteis not changing the vector. Consequently the iterator is valid all the time.
Either you should remove the element for which you have called delete or at least set the vector element to nullptr.
typename std::vector<T*>::iterator it;
for (it=std::vector<T*>::begin();it!=std::vector<T*>::end();it++)
{
delete *it;
*it = nullptr; // Only needed when you don't erase the vector element
}
That solution requires that you always check for nullptr before using any element of the vector.
In most cases the best solution is however to remove the element from the vector.
In the case where you destroy all elements by calling delete on every element, simply call clear on the vector after the loop.
For example, for you first two questions which appeared to regard clearing a vector
std::vector<int> v;//maybe have elements or not...
Clear the vector by calling clear
v.clear();
If you wish to remove elements while you walk over the vector which satisfy a condition (i.e. a predicate) using v.erase(it) you need to be careful. Fortunately, erase returns the iterator after the removed position, so something like
for (auto it=v.begin();it!=v.end();)
{
if (predicate(it))
it = v.erase(it);
else
++it;
}
Obviously you can use std::remove_if instead (with std::erase) if you want to shrink your vector slightly, avoiding a "awfully performant hand-written remove_if" as a commenter described this.
If you want to delete things as you loop round, you can either hand-do a loop yourself or use an algorithm - but smart pointers make far more sense. I suspect you are wanting to delete the items possibly in addition to shrinking the vector since you said " I found that element of this vector can be used after its deleting". If you do want to store raw pointers in a container, just delete the items, you don't the the if in your suggested code. You need to consider when you plan on doing this.
Note: changing the contents of an iterator doesn't invalidate an iterator.
You should consider using smart pointers, for exception safety etc.
I have a vector and it is going to hold 10 objects maximum. When I create the vector I believe it creates 10 "blank" objects of the class it will hold. Therefore I have a problem because my code relies on checking that a vector element is null and obviously it is never null.
How can I check whether a vector object contains an element I inserted, or one of the default constructor "blank" objects upon initialisation?
Is there a technique round this?
(I need to check for null because I am writing a recursive algorithm and the termination point, when the recursive function returns is when an object in the vector is null)
An instance of a class cannot be null. Only a pointer.
You do, however, have size() that you can use.
typedef stdd::vector<SomeClass> vec;
//define some vec, v
for (vec::size_type i = 0, s = vec.size(); i < s; ++i) {
//do something with v[i]
}
With a recursive function you could use this idea by passing along a maximum index.
void recursiveFunc(vec& v, vec::size_type s);
Then when checking your condition to recurse, you would need to check "am I at the end of the vector?"
Alternatively, instead of working on indexes, you could use iterators:
template <typename Iterator>
void recursiveFunc(Iterator begin, const Iterator& end);
If done correctly (and if possible in your situation), this could decouple your manipulation from being aware of the underlying data being stored in a vector.
The loop to go over the vector would then look like:
while (begin != end) {
//do something with *begin
++begin;
}
std::vector only inserts "real" objects. It (at least normally) allocates raw memory, and uses placement new to construct objects in that memory as needed. The only objects it'll contain will be the ones you put there.
Of course, if you want to, you can create a vector containing a number of copies of an object you pass to the constructor. Likewise, when you resize a vector, you pass an object it'll copy into the new locations if you're making it larger.
Neither of those is really the norm though. In a typical case, you'll just create a vector, which will start out containing 0 objects. You'll use push_back to add objects to the vector. When you search through the vector, the only objects there will be the ones you put there with push_back, and you don't need to worry about the possibility of it containing any other objects.
If you just want to check whether the vector is empty, you can just use:
if (your_vector.empty())
...which will (obviously enough) return true if it's empty, and false if it contains at least one object.
As #Corbin mentioned, size() will return the number of elements in the vector. It is guaranteed not to have any holes in between(contiguous), so you assured vector[vector.size()] is empty.
I have a the following code.
vector<IRD>* irds = myotherobj->getIRDs();//gets a pointer to the vector<IRD>
for(vector<IRD>::iterator it = irds->begin(); it < irds->end(); it++)
{
IRD* ird = dynamic_cast<IRD*>(it);
ird->doSomething();
//this works (*it).doSomething();
}
This seems to fail...I just want to get the pointer to each element in the vector without using (*it). all over.
How do I get the pointer to the object?
When I iterate over the vector pointer irds, what exactly am I iterating over? Is it a copy of each element, or am I working with the actual object in the vector when I say (*it).doSomething(),
Why do you want to get a pointer?
Use a reference:
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
IRD & ird = *it;
ird.doSomething();
}
Alternatively:
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
it->doSomething();
}
Also, as everyone said, use != when comparing iterators, not <. While it'll work in this case, it'll stop working if you use a different container (and that's what iterators are for: abstracting the underlying container).
You need to use != with iterators to test for the end, not < like you would with pointers. operator< happens to work with vector iterators, but if you switch containers (to one like list) your code will no longer compile, so it's generally good to use !=.
Also, an iterator is not the type that it points to, so don't try to cast it. You can use the overloaded operator-> on iterators.
vector<IRD>* irds = myotherobj->getIRDs();//gets a pointer to the vector<IRD>
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
it->dosomething();
}
Dereference the iterator to get a reference to the underlying object.
vector<IRD>* irds = myotherobj->getIRDs();
for(vector<IRD>::iterator it = irds->begin(); it != irds->end(); ++it)
{
IRD& ird = *it;
ird.doSomething();
// alternatively, it->doSomething();
}
First consider whether you actually need a pointer to the element or if you're just trying to kind of use iterators but kind of avoid them. It looks like you're trying to code C in C++, rather than coding C++. In the example you gave, it seems like rather than converting to a pointer and then working with that pointer, why not just use the iterator directly? it->doSomething() instead of ird->doSomething().
If you're thinking that you need to save that pointer for later to use on the vector after doing some work, that's potentially dangerous. Vector iterators and pointers to elements in a vector can both be invalidated, meaning they no longer point to the vector, so you are basically attempting to use memory after you've freed it, a dangerous thing to do. A common example of things that can invalidate an iterator is adding a new element. I got into the mess of trying to store an iterator and I did a lot of work to try to make it work, including writing a "re_validate_iterator()" function. Ultimately, my solution proved to be very confusing and didn't even work in all cases, in addition to not being scalable.
The solution to trying to store the position of the vector is to store it as an offset. Some integer indicating the position within the vector that your element is at. You can then access it with either myvector.begin() + index if you need to work with iterators, or myvector.at (index) if you want a reference to the element itself with bounds checking, or just myvector [index] if you don't need bounds checking.
You can get a pointer from an iterator by doing &*it. You get a pointer to the actual IRD object stored inside the vector. You can modify the object through the pointer and the modification will "stick": it will persist inside the vector.
However, since your vector contains the actual objects (not pointers to objects) I don't see any point in dynamic_cast. The type of the pointer is IRD * and it points to IRD object.
The only case when the dereferenced iterator might refer to a copy (or, more precisely, to a proxy object) is vector<bool>, which might be implemented as a bit-vector.
When I iterate over the vector pointer irds, what exactly am I iterating over? Is it a copy of each element, or am I working with the actual object in the vector when I say (*it).doSomething(),
When you iterate over a vector you work with the object itself, not a copy of it.
The usual idiom is &*it to get a pointer. Dynamic casts have nothing to do with it.
Let's say I have this code:
std::vector<Object*> objects;
std::vector<Object*>::iterator iter;
for (iter = objects.begin(); iter != objects.end(); iter++) {
if (condition)
objects.push_back(new Object());
}
However, when push_back occurs, iter becomes undereferenceable. Without resetting iter, how do I keep it dereferenceable? If I have to reset it, is there an easy way to do it so iter goes back to where it was before?
I would recommend that you simply access it by index. This completely eliminates the issue.
If you absolutely must use iterators for this:
std::vector<Object*> objects;
std::vector<Object*> newObjects;
std::vector<Object*>::iterator iter;
for (iter = objects.begin(); iter != objects.end(); ++iter)
{
if (condition)
{
newObjects.push_back(new Object());
}
}
std::copy(newObjects.begin(), newObjects.end(), back_inserter<vector<Object*> >(objects));
You will have to result to an old fashioned for loop with numerical indices. Either that, or reserve() the vector before the loop runs to guarantee it won't resize.
Also, raw pointers? Srsly.
The iterator only becomes invalidated if the vector has to reallocate more memory.
To prevent the reallocation of memory pre-allocate all the memory you need with reserve() (assuming you know this size when you allocate the vector).
The easier solution keep an indirect reference to the member (an index in the array).
ยง23.1/11:
Unless otherwise specified (either
explicitly or by defining a function
in terms of other functions), invoking
a container member function or passing
a container as an argument to a
library function shall not invalidate
iterators to, or change the values of,
objects within that container.
However, it is not explicitly specified, that std::vector::push_back invalidates any iterators.
As most of the other answers already say, you're probably better accessing the vector by index in this case.
However, for completeness: std::list iterators don't have this "problem". So using list instead of vector is a possible solution.