Remove object from list by identity - c++

In the following example a std::list stores objects of type Resource (by value, not pointing to). The Resource class does not provide a less than comparison nor equality operator. What is a good way to remove an object by identity (aka memory address). Can i avoid O(n) somehow? Is std::list the right container?
// Definition of the container
std::list<Resource> resources;
// Code
Resource *closedResource = resourceFromList();
for (std::list<Resource>::iterator it = resources.begin(); it != resources.end(); ++it)
{
if (closedResource == &*it)
{
resources.erase(it);
break;
}
}
Edit: Assume that the Resource class implements move semantic. Furthermore the Resource reregisters itself for any "movement" by a kind of Selector (cp. epoll or java.nio.Selector) for event notifications.

You're storing Resource copies in the container, so later finding an element by address doesn't make much sense.
What you can do is save list::iterators since list has the property that iterators are not invalidated by any operation expect explicitly erasing that iterator (or clearing the entire list).

Edit: Sorry, never mind this answer -- if you are certain only to be removing at most one element, then your loop is fine.
Never mind whether comparing by memory address is sensible, but to use list::erase in a loop you have to write it a little differently to avoid incrementing an invalidated iterator:
for (std::list<Resource>::iterator it = resources.begin(); it != resources.end(); )
{
if (want_to_erase) it = resources.erase(it); // advances to the next position
else ++it;
}
If you have no other requirements than look-up by address, you could always just make a std::set or std::unordered_set of elements and implement Resource::operator<() as pointer comparison.
Either way, there are certain conditions on types for them to be eligible as STL container members; as was asked in the comments, do those conditions apply to your Resource class? Otherwise you could just make a container of std::shared_ptr<Resource> and use its inbuilt comparators, and for example make an std::unordered_set<std::shared_ptr<Resource>>.

"Is std::list the right container?"
I doubt it. If this class does not provide a comparison operator you can still provide one yourself
struct resourcecomparation {
const bool operator() (const Resource &l, const Resource &r) const;
}resourcecomparator;
and then you can use, for instance, an std::set<resource, resourcecomparator>.

Related

Can one capture the reallocations of std::vector?

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.

Optimize search in std::deque

I'm doing a program that has a different kind of objects and all of them are children of a virtual class. I'm doing this looking for the advantages of polymorphism that allow me to call from a manager class a certain method of all the objects without checking the specific kind of object it is.
The point is the different kind of objects need sometimes get a list of objects of a certain type.
In that moment my manager class loop thought all the objects and check the type of the object. It creates a list and return it like this:
std::list<std::shared_ptr<Object>> ObjectManager::GetObjectsOfType(std::string type)
{
std::list<std::shared_ptr<Object>> objectsOfType;
for (int i = 0; i < m_objects.size(); ++i)
{
if (m_objects[i]->GetType() == type)
{
objectsOfType.push_back(m_objects[i]);
}
}
return objectsOfType;
}
m_objects is a deque. I know iterate a data structure is normally expensive but I want to know if is possible to polish it a little bit because now this function takes a third of all the time used in the program.
My question is: is there any design pattern or fuction that I'm not taking into account in order to reduce the cost of this operation in my program?
In the code as given, there is just a single optimization that can be done locally:
for (auto const& obj : m_objects)
{
if (obj->GetType() == type)
{
objectsOfType.push_back(obj);
}
}
The rationale is that operator[] is generally not the most efficient way to access a deque. Having said that, I don't expect a major improvement. Your locality of reference is very poor: You're essentially looking at two dereferences (shared_ptr and string).
A logical approach would be to make m_objects a std::multimap keyed by type.
Some things you can do to speed up:
Store the type on the base class, this will remove a somewhat expensive virtual lookup.
If type is a string, etc. change to a
simpel type like an enum or int
A vector is more effiecient to
traverse than a deque
if staying with deque, use iterators or a range based for loop to avoid the random lookups (which are more expensive in deque)
Range based looks like this:
for (auto const& obj : m_objects)
{
if (obj->GetType() == type)
{
objectsOfType.push_back(obj);
}
}
Update: Also I would recommend against using a std::list (unless for some reason you have to) as it is not really performing well in many cases - again std::vector springs to the rescue !

Is it safe to hold pointers to iterators in C++?

I will ask the question first and the motivation next, and finally an illustrative code sample which compiles and executes as expected.
Question
If I can assure myself that an iterator will not get invalidated in the duration when I will be needing to use it, is it safe to hold a pointer to an iterator (e.g. a pointer to a list<int>::iterator).
Motivation
I have multiple containers and I need direct cross references from items held in one container to the corresponding items held in another container and so on. An item in one container might not always have a corresponding item in another container.
My idea thus is to store a pointer to an iterator to an element in container #2 in the element stored in container #1 and so forth. Why? Because once I have an iterator, I can not only access the element in container #2, but if needed, I can also erase the element in container #2 etc.
If there is a corresponding element in container #2, I will store a pointer to the iterator in the element in container #1. Else, this pointer will be set to NULL. Now I can quickly check that if the pointer to the iterator is NULL, there is no corresponding element in container #2, if non-NULL, I can go ahead and access it.
So, is it safe to store pointers to iterators in this fashion?
Code sample
#include <iostream>
#include <list>
using namespace std;
typedef list<int> MyContainer;
typedef MyContainer::iterator MyIterator;
typdef MyIterator * PMyIterator;
void useIter(PMyIterator pIter)
{
if (pIter == NULL)
{
cout << "NULL" << endl;
}
else
{
cout << "Value: " << *(*pIter) << endl;
}
}
int main()
{
MyContainer myList;
myList.push_back(1);
myList.push_back(2);
PMyIterator pIter = NULL;
// Verify for NULL
useIter(pIter);
// Get an iterator
MyIterator it = myList.begin();
// Get a pointer to the iterator
pIter = & it;
// Use the pointer
useIter (pIter);
}
Iterators are generally handled by value. For instance, begin() and end() will return an instance of type iterator (for the given iterator type), not iterator& so they return copies of a value every time.
You can of course take an address to this copy but you cannot expect that a new call to begin() or end() will return an object with the same address, and the address is only valid as long as you hold on to the iterator object yourself.
std::vector<int> x { 1, 2, 3 };
// This is fine:
auto it = x.begin();
auto* pi = &it;
// This is not (dangling pointer):
auto* pi2 = &x.begin();
It rarely makes sense to maintain pointers to iterators: iterators are already lightweight handles to data. A further indirection is usually a sign of poor design. In your example in particular the pointers make no sense. Just pass a normal iterator.
The problem with iterators is that there are a lot of operations on containers which invalidate them (which one depend on the container in question). When you hold an iterator to a container which belongs to another class, you never know when such an operation occurs and there is no easy way to find out that the iterator is now invalid.
Also, deleting elements directly which are in a container which belongs to another class, is a violation of the encapsulation principle. When you want to delete data of another class, you should better call a public method of that class which then deletes the data.
Yes, it is safe, as long as you can ensure the iterators don't get invalidated and don't go out of scope.
Sounds scary. The iterator is an object, if it leaves scope, your pointer is invalid. If you erase an object in container #2, all iterators may become invalid (depending on the container) and thus your pointers become useless.
Why don't you store the iterator itself? For the elements in container #1 that don't refer to anything, store container2.end().
This is fine as long as iterators are not invalidated. If they are, you need to re-generate the mapping.
Yes it is possible to work on pointers to iterators like it is to other types but in your example it is not necessary since you can simple pass the pass the original iterator as reference.
In general it is not a good idea to store iterators since the iterator may become invalid as you modify the container. Better store the containers and create iterators as you need them.

Hybrid vector/list container?

I'm in need of a container that has the properties of both a vector and a list. I need fast random access to elements within the container, but I also need to be able to remove elements in the middle of the container without moving the other elements. I also need to be able to iterate over all elements in the container, and see at a glance (without iteration) how many elements are in the container.
After some thought, I've figured out how I could create such a container, using a vector as the base container, and wrapping the actual stored data within a struct that also contained fields to record whether the element was valid, and pointers to the next/previous valid element in the vector. Combined with some overloading and such, it sounds like it should be fairly transparent and fulfill my requirements.
But before I actually work on creating yet another container, I'm curious if anyone knows of an existing library that implements this very thing? I'd rather use something that works than spend time debugging a custom implementation. I've looked through the Boost library (which I'm already using), but haven't found this in there.
If the order does not matter, I would just use a hash table mapping integers to pointers. std::tr1::unordered_map<int, T *> (or std::unordered_map<int, unique_ptr<T>> if C++0x is OK).
The hash table's elements can move around which is why you need to use a pointer, but it will support very fast insertion / lookup / deletion. Iteration is fast too, but the elements will come out in an indeterminate order.
Alternatively, I think you can implement your own idea as a very simple combination of a std::vector and a std::list. Just maintain both a list<T> my_list and a vector<list<T>::iterator> my_vector. To add an object, push it onto the back of my_list and then push its iterator onto my_vector. (Set an iterator to my_list.end() and decrement it to get the iterator for the last element.) To lookup, look up in the vector and just dereference the iterator. To delete, remove from the list (which you can do by iterator) and set the location in the vector to my_list.end().
std::list guarantees the elements within will not move when you delete them.
[update]
I am feeling motivated. First pass at an implementation:
#include <vector>
#include <list>
template <typename T>
class NairouList {
public:
typedef std::list<T> list_t;
typedef typename list_t::iterator iterator;
typedef std::vector<iterator> vector_t;
NairouList() : my_size(0)
{ }
void push_back(const T &elt) {
my_list.push_back(elt);
iterator i = my_list.end();
--i;
my_vector.push_back(i);
++my_size;
}
T &operator[](typename vector_t::size_type n) {
if (my_vector[n] == my_list.end())
throw "Dave's not here, man";
return *(my_vector[n]);
}
void remove(typename vector_t::size_type n) {
my_list.erase(my_vector[n]);
my_vector[n] = my_list.end();
--my_size;
}
size_t size() const {
return my_size;
}
iterator begin() {
return my_list.begin();
}
iterator end() {
return my_list.end();
}
private:
list_t my_list;
vector_t my_vector;
size_t my_size;
};
It is missing some Quality of Implementation touches... Like, you probably want more error checking (what if I delete the same element twice?) and maybe some const versions of operator[], begin(), end(). But it's a start.
That said, for "a few thousand" elements a map will likely serve at least as well. A good rule of thumb is "Never optimize anything until your profiler tells you to".
Looks like you might be wanting a std::deque. Removing an element is not as efficient as a std::list, but because deque's are typically created by using non-contiguous memory "blocks" that are managed via an additional pointer array/vector internal to the container (each "block" would be an array of N elements), removal of an element inside of a deque does not cause the same re-shuffling operation that you would see with a vector.
Edit: On second though, and after reviewing some of the comments, while I think a std::deque could work, I think a std::map or std::unordered_map will actually be better for you since it will allow the array-syntax indexing you want, yet give you fast removal of elements as well.

Changing object members via an iterator?

Been a while since I've used C++. Can I do something like this?:
for (vector<Node>::iterator n = active.begin(); n!=active.end(); ++n) {
n->ax /= n->m;
}
where Node is an object with a few floats in it?
If written in Java, what I'm trying to accomplish is something similar to:
for (Node n : this.active) {
n.ax /= n.m;
}
where active is an arrayList of Node objects.
I think I am forgetting some quirk about passing by reference or something throws hands in the air in desperation
Yes. This syntax basically works for almost all STL containers.
// this will walk it the container from the beginning to the end.
for(container::iterator it = object.begin(); it != object.end(); it++)
{
}
object.begin() - basically gives an iterator the first element of the container.
object.end() - the iterator is set to this value once it has gone through all elements. Note that to check the end we used !=.
operator ++ - Move the iterator to the next element.
Based on the type of container you may have other ways to navigate the iterator (say backwards, randomly to a spot in the container, etc). A good introduction to iterators is here.
Short answer: yes, you can.
The iterator is a proxy for the container element. In some cases the iterator is literally just a pointer to the element.
Your code works fine for me
#include <vector>
using std::vector;
struct Node{
double ax;
double m;
};
int main()
{
vector<Node> active;
for (vector<Node>::iterator n = active.begin(); n!=active.end(); ++n) {
n->ax /= n->m;
}
}
You can safely change an object contained in a container without invalidating iterators (with the associative containers, this applies only to the 'value' part of the element, not the 'key' part).
However, what you might be thinking of is that if you change the container (say by deleting or moving the element), then existing iterators might be invalidated, depending on the container, the operation being performed, and the details of the iterators involved (which is why you aren't allowed to change the 'key' of an object in an associative container - that would necessitate moving the object in the container in the general case).
In the case of std::vector, yes, you can manipulate the object simply by dereferencing the iterator. In the case of sorted containers, such as a std::set, you can't do that. Since a set inserts its elements in order, directly manipulating the contents isn't permitted since it might mess up the ordering.
What you have will work. You can also use one of the many STL algorithms to accomplish the same thing.
std::for_each(active.begin(), active.end(), [](Node &n){ n.ax /= n.m; });