Is it possible to access an element of an STL linked list directly by it's pointer? My program requires quick insertion, removal and access of elements.
STL containers use iterators instead of pointers. If you have an iterator that points to an element of your linked list, you can access element's data through it, insert at the iterator's position using the list's insert method, and delete at iterator's position using the erase method.
Instead of using STL linked list, you may want to define your own linked list implementation using pointers. For example:
template <class E>
struct Node {
E data;
Node * next;
};
So define a Node class that will be an element in the linked list. As Kerrek SB suggested, redesigning the program with iterators in mind might be quicker and better in the long term.
Related
Boost.Intrusive can get an Iterator out of an Object-Ref or Object-Pointer in constant time (see here: https://www.boost.org/doc/libs/1_72_0/doc/html/intrusive/usage_when.html). How does that work? Why is this not possible for standard containers?
Intrusive containers by definition have the information contained inside of elements to know how they're located in the container. A simple example is an intrusive linked list:
struct Object {
Object* next;
int some_data;
};
Obviously, if I have a reference or pointer to Object, I can easily find the next field, and from there, move to the next element, this is just accessing a member, which is O(1), thus constant time iterators.
With non-intrusive containers, it would look like this:
struct Object {
int some_data;
};
Suppose I have a std::vector of these, and a pointer or reference to Object, I can't work backwards from that to where it is in the std::vector without scanning the container to find it (a O(n) operation).
So I have a custom linked list. Every node is a struct which has a next pointer to a the next node and the last->next is null.
struct nodo {
T value;
nodo *next;
nodo() : next(0) {}
nodo(const T &v, nodo *n=0): value(v), next(n){}
};
I want to implement iterators in my class and since my class supports random access with operator[] then I chose to implement Random Access Iterators.
Now, my problem is on the following operator:
difference_type operator-(const iterator &other) {
}
It returns the number of elements between this iterator and other iterator, but I'm not sure what's the best to implement it.
Linked lists are not random access containers. You cannot meaningfully implement random access iteration, nor a conventional operator[](size_t) for regular single or double linked lists.
People who use your list class will not expect random access iteration, and will be confused by it. People who use your "random access" iterators and find out they don't conform to standards and conventions will also be confused.
A RandomAccessIterator is a BidirectionalIterator that can be moved to point to any element in constant time.
Only if your container can access elements by index in constant time should you expose random access interfaces.
It seems to me given what I know about linked lists that this should be possible but I haven't found anywhere that has the answer so I'm asking here.
Given two iterators to items in the same list. I'd like to take the item pointed to by iterator "frm" and "insert" it into the list before the item pointed to by iterator "to".
It seems that all that is needed is to change the pointers on the items in the list pointing to "frm" (to remove "frm"), then changing the pointers on the item pointing at "to" so that it references "frm" then changing the pointers on "frm" node to point to "to".
I looked everywhere for this and couldn't find an answer.
NOTE that I cannot use splice as I do not have access to the list only the iterators to the items in the list.
template <typename T>
void move(typename std::list<T>::iterator frm, typename std::list<T>::iterator to) {
//remove the item from the list at frm
//insert the item at frm before the item at to
}
Iterators contain the minimal information required to point to a piece of data, what you are missing is the fact that linked lists have other bookkeeping that go along with it as well, so essentially the list class looks something like the following
template <typename Type>
class list {
int size; // for O(1) size()
Type* head;
Type* tail;
class Iterator {
Type* element;
// no back pointer to list<Type>*
};
...
};
And to remove an element from the list you would need to update those data members as well. And to do that an iterator must contain a back pointer to the list itself, which is not required as per the interface offered for most iterators. Notice also that the algorithms in the STL do not actually modify the bookkeeping for the containers the operate on, only maybe swap elements, and rearrange things.
I would encourage you took look into the <algorithm> header, as well as into facilities like std::back_inserter and std::move_iterator to get an idea of how iterators are wrapped to actually modify the container they represent.
The implementation of this is implementation defined but the c++ standard allows the use of iter_swap though it doesn't do this exactly. This maybe optimized to swap the pointers on the values held in the linked list similar to what I have described effectively reordering the items in the list without a full swap needed.
iter_swap() versus swap() -- what's the difference?
class SororityBeerExpo{
public:
std::list<LaysCrankdPepChip> m_chipList;
void destroyChip(LaysCrankdPepChip * target)
{
// m_chipList needs to erase that which is pointed at by target but
// erase() requires an iterator which is not what we got to work with
// remove() needs a value which is not right either
}
}
My question, is how would one delete an element in a list, with a pointer that points to that element? I would like to do this without using Iterators in place of pointers.
You can do a [linear] search for the element in the list, comparing the address of each element to your pointer (std::find_if will fit the bill). Of course, you will still be using an iterator in the end, because that's what list::erase needs.
You can't do it directly (although see Benjamin's answer for an indirect way of doing it). A list node contains more data than just the object being contained (e.g. pointers to the previous and next nodes), but your raw pointer only points to the contained object.
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.