2012-05-20 15 views
28

Rozumiem normalne przeciążenie operatora. Kompilator może tłumaczyć je bezpośrednio na wywołanie metody. Nie jestem bardzo pewny co do operatora ->. Pisałem swój pierwszy niestandardowy iterator i czułem potrzebę korzystania z operatora ->. Wziąłem przyjrzeć kodu źródłowego STL i wdrożone moje własne jak niej:W jaki sposób przeciążanie operatorów strzałek-> działa wewnętrznie w C++?

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

Wtedy możemy użyć instancji MyClassIterator jak:

myClassIterator->APublicMethodInMyClass(). 

Wygląda na to, że kompilator robi dwa kroki tutaj. 1. Wywołaj metodę ->(), aby uzyskać tymczasową zmienną MyClass *. 2. Wywołaj APublicMethodInMyClass na zmiennej temp za pomocą operatora ->.

Czy moje zrozumienie jest prawidłowe?

Odpowiedz

23
myClassIterator->APublicMethodInMyClass() 

to nic innego, co następuje:

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

Pierwsze wezwanie do przeciążonego operator-> dostaje wskaźnik pewnego typu, który ma dostępną (z call-miejscu) funkcję składową o nazwie APublicMethodInMyClass(). Zwykłe reguły wyszukiwania funkcji są stosowane, aby rozwiązać APublicMethodInMyClass(), oczywiście w zależności od tego, czy jest to wirtualne czy nie.

Nie zawsze istnieje zmienna tymczasowa; kompilator może, ale nie musi, kopiować wskaźnik zwracany przez . Najprawdopodobniej zostanie to zoptymalizowane. Tymczasowe obiekty typu MyClass nie zostaną utworzone.

Zwykłe zastrzeżenia dotyczą także m_iterator - upewnij się, że połączenia nie mają dostępu do unieważnionego iteratora (na przykład, jeśli używasz vector).

+2

Właściwie to 'myClassIterator.operator ->() -> APublicMethodInMyClass()' –

+0

Dzięki za wyjaśnienie. Czy myClassIterator.operator ->(). APublicMethodInMyClass() beClassIterator.operator ->() -> APublicMethodInMyClass()? Zwracany typ ->() to MyClass * – Ryan

+0

. Dzięki Seth. – Ryan

66

Ma specjalną semantykę w tym języku, że po przeładowaniu ponownie przypisuje się do wyniku. Podczas gdy pozostałe operatory są stosowane tylko raz, operator-> zostanie zastosowane przez kompilator tyle razy, ile potrzeba, aby dostać się do surowego wskaźnika i jeszcze raz, aby uzyskać dostęp do pamięci przywołanej przez ten wskaźnik.

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

W poprzednim przykładzie, w wyrażeniu d->foo() kompilator zajmie obiekt d i zastosować operator-> do niego, co daje obiekt typu C będzie następnie ponownie operatorowi uzyskać instancją B, ponownie aplikuj i przejdź do A*, po czym usunie obiekt i dotrze do wskazanych danych.

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

Czy możesz wskazać mi odniesienie do tego? Nie można znaleźć żadnego. Nikt inny nawet o tym nie wspomina. –

+7

@MilindR: 13.5.6/1 [...] * Wyrażenie x-> m interpretowane jest jako '(x.operator ->()) -> m' dla obiektu klasy x typu T, jeśli' T: : operator ->() 'istnieje i jeśli operator jest wybierany jako najlepsza funkcja dopasowania przez mechanizm zabezpieczenia przed przeciążeniem * Jeśli' x-> operator ->() 'daje wskaźnik, zostaje dereferencjonowany, jeśli daje obiekt typ, który przeciąża 'operator ->()' ten operator zostaje wywołany. –

+2

To powinna być poprawna odpowiedź. –