2010-12-19 4 views
5

Meine aktuelle Aufgabe ist das Schreiben einer Liste mit Iteratoren. Die Liste ist kein so großes Problem wie das Erstellen der Iterator-Klasse.Semantik von -> Operator in Listen (und allgemein C++)

Aus ein paar Quellen habe ich gesehen, dass ich zwei Operatoren in meiner Iteratorklasse definieren muss: operator* und operator->.

Großartig so weit! meine Iterator Struktur Gesetzt ist 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; } 
}; 

mit ListElem

// Nested struct of List 
struct ListElem 
{ 
    T data; 

    ListElem *next; 
    ListElem *prev; 
}; 

zu sein ich kann ich mache etwas falsch massiv (als Doppel dereferenzierenden dies sehen zu einem & (* pCurr _-> Daten führen würde)

Mein Hauptproblem ist nicht zu verstehen, was -> in diesem Fall eigentlich tun soll.Wird es dem Benutzer Zugriff auf die Klasse ListElem geben? Wenn das der Fall ist, warum kann ich nicht schreibe einfach

ListElem *operator->() { return pCurr_; } 

anstatt einen Zeiger zurückzugeben? Mein Verständnis dieser beiden Operatoren wie in meiner Liste verwendet (und hoffentlich auch STL-Listen) ist, dass:

operator*() // Return data pointed to by iterator; pCurr_->data; 
operator->() // Grant access to data-holding structure; pCurr; 

Ist das richtig, oder was bin ich nicht bekommen? (Und hat -> einen Eigennamen?)

Antwort

5

Was auch immer Sie tun, (*something).somethingElse sollte something->somethingElse entsprechen. Letzteres ist nur eine kurze Syntax für das erstere. Daher

T& operator*() { return pCurr_->data; } 
T* operator->() { return &**this; } 

ist in Ordnung, weil *this nur dereferenziert this, die den Typ hat _Iter*, nicht _Iter, so dass keine operator*() Anruf erfolgt.Dann dereferenzieren Sie *this, so erhalten Sie pCurr->data, dann nehmen Sie seine Adresse, so erhalten Sie & pCurr-> Daten. Aber es wäre viel klarer schreiben einfach:

T& operator*() { return pCurr_->data; } 
T* operator->() { return &pCurr->data; } 

Nun, dies

ListElem *operator->() { return pCurr_; } 

falsch ist, denn wenn operator*() kehrt T&, operator->() sollte T* zurückkehren, das ist, was es entworfen wurde. Wenn Sie wirklich ListItem statt seiner Daten Zugriff gewähren möchten (was je nach Design sinnvoll ist oder nicht, aber in Ihrem Fall sieht es nicht so aus), sollten Sie auch operator*() neu definieren, um folgendes zu erhalten:

Beachten Sie, dass es keine Sprachanforderung ist, es ist nur, wie Sie Ihre Klasse entwerfen, um eine unübersichtliche Schnittstelle zu vermeiden.

+0

In der Tat vergesse ich oft, dass "das" eigentlich ein Zeiger ist. Sehr nervig: D – IAE

1

operator-> gibt einen Zeiger auf das Objekt, auf das der Iterator zeigt, in diesem Fall (anscheinend) pCurr_->data.

T *operator->() { return &(pCurr_->data); } 

Es sollte die Adresse des von operator*() als Wert oder Bezugszurückgegebene Objekt zurück.

T &operator*() { return pCurr_->data; } 
// or 
T &operator*() { return *operator->(); } 

operator->() existiert -> mit Iteratoren zu implementieren (mit dem Verhalten es für Zeiger hat) und ist notwendig, weil operator* ein Objekt als Wert zurückgeben kann statt durch Bezugnahme eingeschlossen.

Beachten Sie, dass Sie im Iterator keinen Zeiger auf List speichern müssen, um die erforderliche Funktionalität zu erhalten.

+1

Ich habe meine Frage mit der Struktur ListElem aktualisiert. Und aaaah, indem wir einen Zeiger auf Foo bereitstellen, können wir jetzt alle Foos-Methoden verwenden! Daher gebe ich das ListElem nicht selbst zurück, sondern übergebe die Funktionalität des Foo-Datenelements an den Benutzer! Das macht Sinn. – IAE

+0

@SoulBeaver: aktualisiert meine Antwort. –

+0

Ja, Sie geben das zugrunde liegende Datenelement zurück, das der Client verwenden soll. Denken Sie daran, dass Iteratoren eine Verallgemeinerung von Zeigern sind und ähnlich auf ein Datenelement zeigen (manchmal sehr indirekt; ich habe gerade einen Iterator entworfen, der Zeiger auf polymorphe Objekte hält, die wiederum verschiedene Arten von Iteratoren enthalten). –

1

Ihre Haupt Richtlinie sollte sein, dass

(*iter).hello(); 
iter->hello(); 

sollten beide das gleiche tun. Das erwartet der Benutzer. Die Rückgabe von ListElem gibt dem Benutzer nichts. Der Benutzer sollte nicht einmal die Details der Implementierung von ListElem kennen.