Frage Wie funktioniert arrow-> operator overloading intern in c ++?


Ich verstehe die normale Überlastung des Operators. Der Compiler kann sie direkt in einen Methodenaufruf übersetzen. Ich bin mir nicht sehr klar über den -> Betreiber. Ich schrieb meinen ersten benutzerdefinierten Iterator und ich fühlte mich wie der - Operator. Ich habe mir den Quellcode von stl angesehen und meinen eigenen Code wie folgt implementiert:

MyClass* MyClassIterator::operator->() const
{
    //m_iterator is a map<int, MyClass>::iterator in my code.
    return &(m_iterator->second);
}

Dann kann ich eine Instanz von MyClassIterator verwenden wie:

myClassIterator->APublicMethodInMyClass().

Sieht so aus, als würde der Compiler hier zwei Schritte machen. 1. Rufen Sie die Methode -> () auf, um eine temporäre MyClass * -Variable zu erhalten. 2. Rufen Sie die Variable APublicMethodInMyClass für die Variable temp auf und verwenden Sie ihren Operator ->.

Ist mein Verständnis richtig?


32
2018-05-20 22:36


Ursprung


Antworten:


myClassIterator->APublicMethodInMyClass()

ist nichts als das Folgende:

myClassIterator.operator->()->APublicMethodInMyClass()

Der erste Anruf zum Überladen operator-> Ruft einen Zeiger eines Typs ab, der eine zugängliche (von Ihrer Aufrufseite) Elementfunktion aufgerufen hat APublicMethodInMyClass(). Die üblichen Regeln zum Nachschlagen von Funktionen werden befolgt, um sie zu lösen APublicMethodInMyClass()natürlich, je nachdem, ob es sich um ein virtuelles oder nicht.

Es gibt nicht unbedingt eine temporäre Variable; der Compiler kann kopieren oder nicht kopieren der Zeiger zurückgekommen von &(m_iterator->second). Aller Wahrscheinlichkeit nach wird dies weg optimiert werden. Keine temporären Objekte vom Typ MyClass wird aber erstellt werden.

Die üblichen Vorbehalte gelten auch für m_iterator - Stellen Sie sicher, dass Ihre Anrufe nicht auf einen ungültigen Iterator zugreifen (d. h., wenn Sie ihn verwenden vector beispielsweise).


26
2018-05-20 22:46



Das operator-> hat eine spezielle Semantik in der Sprache, in der es sich, wenn es überladen ist, erneut auf das Ergebnis anwendet. Während der Rest der Operatoren nur einmal angewendet wird, operator-> wird vom Compiler so oft wie nötig angewendet, um zu einem rohen Zeiger zu gelangen, und noch einmal, um auf den von diesem Zeiger referenzierten Speicher zuzugreifen.

struct A { void foo(); };
struct B { A* operator->(); };
struct C { B operator->(); };
struct D { C operator->(); };
int main() {
   D d;
   d->foo();
}

Im vorherigen Beispiel im Ausdruck d->foo() Der Compiler wird das Objekt übernehmen d und bewerben operator-> dazu, was ein Objekt des Typs ergibt CAnschließend wird der Operator erneut angewendet, um eine Instanz von B, erneut bewerben und zu A*Danach wird das Objekt dereferenziert und zu den angegebenen Daten gebracht.

d->foo();
// expands to:
// (*d.operator->().operator->().operator->()).foo();
//   D            C            B           A*

70
2018-05-21 02:35