Почему перегруженный оператор удаления ссылок не работает так же, как оператор стрелки? - PullRequest
0 голосов
/ 19 апреля 2020

Снова чтение С ++, учебник 5 изд. теперь я читал о перегрузке операторов доступа членов. Мне все ясно, кроме:

struct A
{
    int& operator* () {return *p;}
    void foo()const{cout << "A::foo()\n";}
    int* p = new int(5);
};

struct B
{
    A& operator*(){return a;}
    A* operator->(){return &a;}
    A a{};
};

struct C
{
    B& operator*(){return b;}
    B& operator->(){return b;}
    B b{};
};


int main()
{

    C c;
    //cout << *c << endl; // error.
    c->foo(); // works

}
  • Я понял, что оператор стрелки может быть перегружен и должен быть функцией-членом. И если я вижу выражение, как в main c->foo(), я могу думать, что c является либо встроенным указателем на объект типа класса, который имеет функцию-член с именем foo, поэтому извлекаем его. Или (как в случае с main) c - это объект типа класса, который определил свой собственный ->. Так как c здесь - это объект, выражение которого вызывает оператор стрелки c, который возвращает объект класса B, который сам вызывает свой оператор стрелки, пока не вернет объект B, который его -> возвращает встроенный в указателе на объект A, и в этом случае на него не ссылаются, и полученный объект используется для извлечения функции foo(). Поэтому он рекурсивно вызывает себя, пока не будет возвращен встроенный указатель, и этот указатель должен указывать на объект, который имеет этот извлеченный член.

  • Что я не понимаю: почему де оператор не работает так же? Так почему же разыменование c не вызывает оператор * для b и т. Д., Пока он возвращает объект, который определил его оператор разыменования?

  • Пожалуйста, не спорьте об утечке памяти в A, цель для краткости.

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

Вот как -> (доступ к элементу) и * (косвенное указание) определены .

Как вы указали, -> будет рекурсивно вызывать -> на все, что он возвращает, пока он не разрешается в указателе. На этом этапе вы можете делать то, что хотели бы сделать с указателем.

Оператор * просто не определен для такой работы. Нет рекурсивного вызова к * для того, что возвращается.

В вашем случае, вы можете добраться до foo, используя косвенное обращение, но вам придется делать это вручную, например:

(**c).foo();

Обратите также внимание, что ваш конкретный пример не компилируется, потому что *c возвращает ссылку на объект типа B, для которого не определено operator<<.

0 голосов
/ 19 апреля 2020

Причина в том, что оператор разыменования по своей природе унарный (возвращает ссылку), поэтому он может быть перегружен, как и любой другой оператор. И это можно использовать рекурсивно, при желании вам просто нужно применить его снова.

OTOH, -> используется больше как бинарный оператор ... но вещь справа это не значение, а имя . Это имя нужно искать где-то. Так что компилятору определенно нужен объект. Таким образом, оператор определяется как унарный. Но он не используется как унарный, поэтому вы можете просто применить его снова, если хотите. Так что это определяется с другой стороны: вы можете положиться на рекурсивное приложение или отказаться от него, возвращая необработанный указатель.

Отказ от ответственности: именно так я вижу это.

...