C++ : vector.erase(this) - c++

I have an std::vector that holds all the objects and is passed to them in every frame, so that every object can access other objects. My question is how can an object can delete itself from the vector?
This doesn't work:
vector.erase(this);
Is there any other solution for this?

erase requires an iterator. this is a pointer available within member functions/class definition. Also, erase needs to be called on an instance of vector -- you cannot use vector.erase.
The object can't delete itself. You will need to pass an iterator. You can use std::find to find if the object exists within the vector and then pass that along.

You need to delete the element by accessing its iterator, this can be done through std::find, or if you know the index (less complexity) through:
vector.begin() + indexOfElement
so that you can delete it:
vector<Type>::iterator it = yourVector.begin() + index;
yourVector.erase(it);
Mind that, since a vector stores its data in an array format, this causes all elements after the one you are deleting to be shifted to the left (which is expensive). Consider using a std::list instead if you don't need random access.

Alternatively you could use a set and use set.erase(this)

As mentioned you can use std::find ( http://www.cplusplus.com/reference/algorithm/find/ ) This requires you to overload the == operator if I'm not mistaken.
For example
std::vector<YourObjectType>::iterator it = find(yourVector.begin(), yourVector.end(), referenceToObject);
bool operator == (const YourObjectType* object) {return object == this; }
And then you just use yourVector.erase(it);

You can do it as:
vec.erase(vec.begin() + (this - &vec.front()));
if you are sure that the element is from the vector indeed

Related

Checking whether element exists in a Vector

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.

Vector iterators

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.

Can I delete a item from std::list based on pointer value?

This code is written on fly, plz ignore syntax mistakes if any.
std::list<MY_STRUCT> myList;
MY_STRUCT theStruct;
myList.push_back( theStruct );
myList.push_back( theStruct );
// assume I store the pointer of the last item (the 2nd item in this case).
MY_STRUCT * item2 = &myList.back();
// I added another item
myList.push_back( theStruct );
// now I want to delete item2 that I stored bases on its pointer.
// Can myList.remove_if(...) help if so how?
I want to delete the middle item in the list by its pointer (assume I have the pointer value).
I know I can iterate through the list and look for this pointer but is there a better way? Does STL provide a function to do it..Can I use remove_if() in this case to delete the item?
Instead of keeping a pointer to the object you want to remove, why not keep an iterator?
std::list<MY_STRUCT>::iterator item2 = --mylist.end();
The remove_if algorithm doesn't actually remove anything, it just shifts stuff around. It has no knowledge of the container that the iterators point to. Of course the member function remove_if of std::list is a different thing altogether as pointed out in the comments.
Sure, list::remove_if uses whatever condition you give it. For example
template <typename T>
struct AddressIs {
T *ptr;
AddressIs(T *ptr) : ptr(ptr) {}
bool operator()(const T &object) const {
return ptr == &object;
}
};
myList.remove_if(AddressIs<MY_STRUCT>(item2));
Mankarse's point is good though - if you can use an iterator instead of a pointer to identify the item you're interested in, then you don't need to mess about with this.
Beware also that we're relying here on the fact that the address of an item in a list stays the same forever. That isn't always true of all collections, for example vector might have to relocate all the data when you call push_back. If it does, then your middle item is no longer pointed to by item2. Each collection documents which operations can invalidate iterators and/or references to elements.
Instead of getting the back item, you could get the end iterator, make sure it's not begin, decrement by one to point to the last item, and then erase that iterator directly whenever you want.
I think remove_if is a little bit of overkill for what zadane is trying to do. All that needs to be accomplished is to save the location or value of an item in order to delete that specific item later.
As Mark suggested you can store the iterator to the object and use it to delete the item with an erase call, like below:
MY_STRUCT struct;
myList.push_back(struct);
myList.push_back(struct);
std::list<MY_STRUCT>::iterator del_it = myList.end() - 1;
myList.erase(del_it);
Or, if your structure has the == operator defined for MY_STRUCT, you can store the value of the object itself and use the remove method
MY_STRUCT struct1;
MY_STRUCT struct2;
myList.push_back(struct1);
myList.push_back(struct2);
myList.remove(struct2);
Of course if you make your list a list of pointers then you don't have to worry about the == operator as it is already defined for pointer types. Just make sure that if you're iterating through the list and call erase, you need to update your iterator with the returned value.
Also, the remove method removes all elements of the passed value, so if you only want to remove 1 item at a time save the iterator and not the value.
This code is untested so I welcome any corrections.

(C++) Can't get deque insert() to work, what am I doing wrong?

I'm getting an error on the insert() part here, and I dunno what I am doing wrong. I've tried different parameters and number of parameters but nothing seems to work.
m_oGameObjectList is a deque of IGameObjects (base class).
m_sPosition is a struct with 3 ints (X, Y, Z).
gameObject is the reference to an object derived from IGameObject.
for (int i = 0; i < m_oGameObjectList.size(); i++)
{
if (gameObject.m_sPosition.Z > m_oGameObjectList[i].m_sPosition.Z)
{
m_oGameObjectList.insert(i, gameObject);
i = m_oGameObjectList.size();
}
}
insert takes an iterator. Use:
m_oGameObjectList.insert(m_oGameObjectList.begin() + i, gameObject);
You'll also need to use pointers in your deque, right now you're slicing - inserting a copy of the IGameObject part of gameObject
Your call to insert should pass an iterator (not an integer index) into the deque. One way you can convert an integer index to a deque iterator is via:
my_deque_iterator iter = m_oGameObjectList.begin();
std::advance(m_oGameObjectList, i);
... though there are several other solutions that work equally well.
You can also use deque functions like push_back and push_front which just take the object you want to put at the front or back of the deque, respectively.
trying to insert an object derived from IGameObject into a deque<IGameObject> won't work as the deque is trying to store a copy of the object in the reference, not the reference itself.
Most of the time, if your trying to store an class hiearchy into a container, you do so by having the container of pointers to the base class.

Does ptr_vector iterator not require increments?

#include <boost/ptr_container/ptr_vector.hpp>
#include <iostream>
using namespace std;
class Derived
{
public:
int i;
Derived() {cout<<"Constructed Derived"<<endl;}
Derived(int ii):i(ii) {cout<<"Constructed Derived"<<i<<endl;}
~Derived() {cout<<"* Destructed Derived"<<i<<endl;}
};
int main()
{
boost::ptr_vector<Derived> pv;
for(int i=0;i<10;++i) pv.push_back(new Derived(i));
boost::ptr_vector<Derived>::iterator it;
for (it=pv.begin(); it<pv.end();/*no iterator increment*/ )
pv.erase(it);
cout<<"Done erasing..."<<endl;
}
Notice that the second for loop does not increment the iterator, yet it iterates and erases all elements. My questions are:
Is my technique of iteration and using the iterator correct?
If iterator increment is not required in the for loop, then where does the increment happen?
Is it better to use an iterator or will an ordinary integer suffice (ie: is there any value-add with using iterators)? (coz I can also erase the 5th element like pv.erase(pv.begin()+5);)
Is there any way to assign a new object to a specific position (let's say the 5th position) of ptr_vector, directly? I'm looking for something like pv[5]=new Derived(5);. Any way of doing that?
A ptr_vector::iterator increments just like a normal random access iterator. In your example, you are able to erase every element without actually incrementing because after you erase an element, every element after it is moved over in the array. So when you erase the 0th element, your iterator now points to the element which used to be the 1st element, but is now the 0th element, and so on. In other words, the iterator is staying in place while the whole vector is shifting over to the left.
This has nothing specifically to do with ptr_vector. Note the same behavior would occur with a plain std::vector.
Also note that using an iterator after you erase the element it points to is dangerous. In your case it works, but it's better to take the return value of ptr_vector::erase so you get a new iterator which is guaranteed to be valid.
for (it = pv.begin(); it != pv.end(); )
it = pv.erase(it);
As for your other questions:
If you only want to erase a specific element, then of course you should erase it directly using pv.erase(pv.begin() + N). To assign a new value to a specific element in the pointer vector, simply say pv[N] = Derived(whatever). You don't need to use new when reassigning a value. The pointer vector will invoke the assignment operator of the object at the index you assign the new value to.
Is my technique of iteration and using the iterator correct?
No, erasing from a container generally invalidates the iterator to the erased item. If it works, this is just a side-effect of the implementation details.
The correct way would be to use the return value of the erase method:
it = pv.erase(it);
However, for emptying the container, you can use the clear member function.
If iterator increment is not required in the for loop, then where
does the increment happen?
It doesn't happen, because you'll always be erasing the first item in the container (by chance, might not work out with other containers).
Is it better to use an iterator or will an ordinary integer suffice (ie:
is there any value-add with using
iterators)? (coz I can also erase the
5th element like
pv.erase(pv.begin()+5);)
In a random-access container you can do that, otherwise not (such as a list).
Is there any way to assign a new object to a specific position (let's
say the 5th position) of ptr_vector,
directly? I'm looking for something
like pv[5]=new Derived(5);. Any way
of doing that?
According to the boost reference:
pv.replace(5, new Derived(5));
This returns the existing pointer in a smart pointer, so it will be automatically deallocated.
(It's curious that this takes an index, not an iterator...).
Or:
pv[5] = Derived(5);
but this will just modify the stored object, not change the pointer.