My current assignment is writing a list with iterators. The list isn't being a problem so much as creating the iterator class is.
From a couple of sources I've seen that I have two operators to define in my iterator class: operator*
and operator->
.
Great so far! Supposing my iterator structure is so
// Nested class of List
class _Iter
{
private:
ListElem *pCurr;
const List *pList;
public:
_Iter(ListElem *pCurr, const List *list)
: pCurr_(pCurr), pList(list)
{}
T& operator*() { return pCurr_->data; }
T* operator->() { return &**this; }
};
with ListElem being
// Nested struct of List
struct ListElem
{
T data;
ListElem *next;
ListElem *prev;
};
I can see I'm doing something massively wrong (as double dereferencing of this would lead to a &(*pCurr_->data), which is not dereferencable.
My main problem is not understanding what -> is actually supposed to do in this case. Should it grant the user access to the ListElem class? If that's the case, why can't I just write
ListElem *operator->() { return pCurr_; }
instead of returning a pointer? My understanding of these two operators as used in my list (and hopefully STL lists) is that:
operator*() // Return data pointed to by iterator; pCurr_->data;
operator->() // Grant access to data-holding structure; pCurr;
Is this correct, or what am I not getting? (And does ->
have a proper name?)
Whatever you do, (*something).somethingElse
should be equivalent to something->somethingElse
. The latter is just a short syntax for the former. Therefore,
T& operator*() { return pCurr_->data; }
T* operator->() { return &**this; }
is fine because *this
just dereferences this
which has the type _Iter*
, not _Iter
, so no operator*()
call is done. Then you dereference *this
, so you get pCurr->data
, then you take its address, so you get &pCurr->data. But it would be much clearer to just write:
T& operator*() { return pCurr_->data; }
T* operator->() { return &pCurr->data; }
Now, this
ListElem *operator->() { return pCurr_; }
is wrong because if operator*()
returns T&
, operator->()
should return T*
, that's what it was designed for. If you really want to grant access to ListItem instead of its data (which may or may not make sense depending on the design, but in your case it looks like it doesn't), then you should also redefine operator*()
to get this:
ListElem& operator*() { return *pCurr_; }
ListElem *operator->() { return pCurr_; }
Note that it is not a language requirement, it is just how you design your class to avoid confusing interface.
Your main guideline should be that
(*iter).hello();
iter->hello();
should both do the same thing. That's what the user expects. Returning ListElem gives nothing to the user. The user shouldn't even know the details of the implementation of ListElem.
operator->
gives a pointer to the object pointed to by the iterator, in this case (apparently) pCurr_->data
.
T *operator->() { return &(pCurr_->data); }
It should return the address of the object returned by operator*()
as a value or reference.
T &operator*() { return pCurr_->data; }
// or
T &operator*() { return *operator->(); }
operator->()
exists to implement ->
with iterators (with the behavior it has for pointers) and is necessary because operator*
may return an object by value instead of by reference.
Note that you don't need to store a pointer to the List
in the iterator to obtain the functionality required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With