Подтвердить что ты не робот

Как перегрузка операторов arrow-> работает внутри С++?

Я понимаю нормальную перегрузку оператора. Компилятор может напрямую перевести их на вызов метода. Я не очень разбираюсь в операторе → . Я писал свой первый пользовательский итератор, и я чувствовал потребность в операторе → . Я взглянул на исходный код stl и внедрил свой собственный:

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

Затем я могу использовать экземпляр MyClassIterator, например:

myClassIterator->APublicMethodInMyClass().

Похоже, компилятор делает два шага здесь. 1. Вызовите метод → (), чтобы получить временную переменную MyClass *. 2. Вызовите APublicMethodInMyClass для переменной temp с помощью оператора → .

Правильно ли я понимаю?

4b9b3361

Ответ 1

myClassIterator->APublicMethodInMyClass()

- не что иное, как следующее:

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

Первый вызов перегруженного operator-> получает указатель некоторого типа, который имеет доступную (от вашего сайта) функцию-член, которая называется APublicMethodInMyClass(). Обычные правила поиска функций следуют для разрешения APublicMethodInMyClass(), конечно, в зависимости от того, является ли это виртуальным или нет.

Существует необязательная временная переменная; компилятор может или не может скопировать указатель, возвращенный &(m_iterator->second). По всей вероятности, это будет оптимизировано. Однако временные объекты типа MyClass будут созданы.

Обычные оговорки также применимы к m_iterator - убедитесь, что ваши вызовы не имеют доступа к недействительному итератору (т.е. если вы используете vector, например).

Ответ 2

operator-> имеет специальную семантику в языке в том, что при перегрузке она снова прибегает к результату. В то время как остальные операторы применяются только один раз, operator-> будет применяться компилятором столько раз, сколько необходимо для доступа к необработанному указателю и еще раз для доступа к памяти, указанной этим указателем.

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

В предыдущем примере в выражении d->foo() компилятор возьмет объект d и применит к нему operator->, который даст объект типа C, затем он повторно применит оператор, чтобы получить экземпляр B, повторно применить и перейти к A*, после чего он будет разыменовывать объект и перейти к указанным данным.

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