Перегрузка операторов доступа членов ->,. * (С++)
Я понимаю большинство перегрузок операторов, за исключением операторов доступа членов ->
, .*
, ->*
и т.д.
В частности, что передается этим операторным функциям и что нужно вернуть?
Как функция оператора (например, operator->(...)
) знает, к какому члену относятся? Может ли это знать? Нужно ли это знать?
Наконец, существуют ли какие-либо соображения, которые необходимо учитывать? Например, при перегрузке чего-то вроде operator[]
, как правило, вам понадобятся как константная, так и неконстантная версия. Операторы доступа к члену требуют константных и неконстантных версий?
Ответы
Ответ 1
->
Это единственное, действительно сложное. Это должна быть нестатическая функция-член, и она не принимает аргументов. Возвращаемое значение используется для выполнения поиска элемента.
Если возвращаемым значением является другой объект типа класса, а не указатель, то последующий поиск элемента также обрабатывается функцией operator->
. Это называется "древовидным поведением". Язык объединяет вызовы operator->
, пока последний не вернет указатель.
struct client
{ int a; };
struct proxy {
client *target;
client *operator->() const
{ return target; }
};
struct proxy2 {
proxy *target;
proxy &operator->() const
{ return * target; }
};
void f() {
client x = { 3 };
proxy y = { & x };
proxy2 z = { & y };
std::cout << x.a << y->a << z->a; // print "333"
}
->*
Это только сложно, потому что в этом нет ничего особенного. Для неперегруженной версии требуется объект указателя на тип класса в левой части и объект указателя на тип члена справа. Но когда вы перегружаете его, вы можете принимать любые аргументы, которые вам нравятся, и возвращать все, что хотите. Это даже не должно быть нестатический член.
Другими словами, это просто обычный двоичный оператор, такой как +
, -
и /
. Смотрите также: Свободно ли оператор- > * перегружает зло?
.*
и .
Они не могут быть перегружены. Уже есть встроенное значение, когда левая сторона имеет тип класса. Возможно, было бы немного иметь смысл определить их для указателя с левой стороны, но комитет по разработке языка решил, что это будет более запутанным, чем полезным.
Перегрузка ->
, ->*
, .
и .*
может заполнять только случаи, когда выражение будет undefined, оно никогда не может изменить значение выражения, которое было бы действительным без перегрузки.
Ответ 2
Вы не можете перегрузить доступ к членству .
(т.е. вторая часть того, что делает ->
). Однако вы можете перегрузить унарный разглашение оператор *
(т.е. Первая часть того, что делает ->
).
Оператор С++ ->
представляет собой объединение двух шагов, и это понятно, если вы считаете, что x->y
эквивалентно (*x).y
. С++ позволяет вам настроить, что делать с частью (*x)
, когда x
является экземпляром вашего класса.
Семантика для перегрузки ->
несколько странная, потому что С++ позволяет вам возвращать обычный указатель (который будет использоваться для поиска заостренного объекта) или возвращать экземпляр другого класса, если этот класс также предоставляет ->
. Когда в этом втором случае поиск объекта разыменования продолжается из этого нового экземпляра.
Ответ 3
Оператор → является специальным.
"У него есть дополнительные, нетипичные ограничения: он должен возвращать объект (или ссылку на объект), который также имеет оператор разыменования указателя, или он должен возвращать указатель, который может использоваться для выбора того, что оператор стрелки указателя указателя указывая на."
Брюс Экель: Мышление CPP Vol-one: operator- >
Дополнительные функции предоставляются для удобства, поэтому вам не нужно вызывать
a->->func();
Вы можете просто сделать:
a->func();
Это делает оператор → отличным от других перегрузок операторов.
Ответ 4
Оператор ->
не знает, на какой элемент указывается, он просто предоставляет объект для фактического доступа к члену.
Кроме того, я не вижу причин, по которым вы не можете предоставить версии const и non-const.
Ответ 5
Когда вы перегружаете оператор → () (здесь не передаются аргументы), то, что на самом деле делает компилятор, вызывает → рекурсивно, пока не вернет фактический указатель на тип. Затем он использует правильный элемент/метод.
Это полезно, например, для создания класса интеллектуального указателя, который инкапсулирует фактический указатель. Вызывается перегруженный оператор- > , делает то, что он делает (например, блокирует безопасность потока), возвращает внутренний указатель, а затем вызовы компилятора → для этого внутреннего указателя.
Что касается константы, на нее были даны ответы в комментариях и других ответах (вы можете и должны предоставить оба).