#c #object #member #dereference
#c #объект #Участник #Разыменование
Вопрос:
Кто-нибудь может объяснить разницу между выполнением чего-то вроде:
a->height();
и
a.height();
Есть ли на самом деле разница?
Комментарии:
1. Просто любопытно, почему есть точки для этого вопроса?
Ответ №1:
В первом примере a — это указатель на объект, во втором примере это сам объект (или ссылка на объект). На самом низком уровне между ними нет четкой разницы.
Комментарии:
1. @AdrianMar, но выполняет ли это ту же операцию?
2. Еще одной возможностью для
a.height()
:a
может быть ссылка на объект. По сути, ссылка — это указатель, но с точки зрения пользовательского кода ссылка является псевдонимом для рассматриваемого объекта.3. Ну да, это просто другой способ доступа к объекту (прямо или косвенно, через его указатель). Метод вызывается практически таким же образом.
4. @Adrian: Подумайте о случае, когда
a
является указателем на экземпляр некоторого полного (т. Е. не абстрактного) базового класса, по сравнению со случаем, когдаa
является экземпляром этого класса. В этом случаеa->height()
вполне может выполняться совершенно иная функция, чем делаетa.height()
.5. @Дэвид Хаммен: Да, но это на самом деле не имело бы отношения к разнице между
.
и->
. Это только иллюстрировало бы нормальное поведение виртуальной функции.
Ответ №2:
Это странный вопрос, если предположить, что мы говорим о встроенном ->
. Это звучит немного как «в чем разница между молотком и яблоком». Обычно вопрос о «разнице» возникает, когда два варианта по крайней мере в некоторой степени взаимозаменяемы, оба применимы одновременно. Итак, люди спрашивали бы о «разнице», чтобы решить, какой вариант использовать. Здесь дело не в этом.
У вас не может быть обоих a->height()
и a.height()
действительных одновременно. Только одно из двух может быть допустимым, в зависимости от типа a
. Т.е. у вас нет выбора, какую версию использовать. Первый вариант (с ->
) применим, если левая сторона является указателем на объект. Второй вариант (с помощью всего лишь .
) применим только тогда, когда левая сторона является самим значением объекта. Итак, вот и все, что нужно сделать.
->
Это просто сокращение для комбинации унарных *
и .
, означающее, что когда a
является указателем a->height()
, это эквивалентно (*a).height()
. Итак, более разумным вопросом было бы о разнице между a->height()
и (*a).height()
. И ответ таков: разницы нет (опять же, пока мы рассматриваем встроенное ->
)
Комментарии:
1. Вы сказали, что
"You can't have both a->height() and a.height() valid at the same time."
на самом деле вы можете иметь оба одновременно : ideone.com/7tZdS2. @Nawaz: Конкретно по этой причине я явно заявляю, что рассматриваю только встроенную
->
, в отличие от перегруженной->
.
Ответ №3:
В основном это сводится к тому, как вы объявили «a». Если вы сделаете:
SomeClass a;
тогда «a» сам по себе является объектом, и вы используете «.» для ссылки на его члены.
Если вы сделаете:
SomeClass * a;
a = [some expression that produces a pointer to a SomeClass object];
тогда «a» — это УКАЗАТЕЛЬ НА объект, а не сам объект. Затем вы используете обозначение «->».
(Есть некоторые потенциальные исключения из приведенных выше правил, но с ними не сталкивается большинство людей.)
Ответ №4:
Также стоит упомянуть, что некоторые объекты, такие как интеллектуальные указатели, поддерживают обе нотации. В этом случае первый будет ссылаться на объект, на который указывает указатель, а второй будет ссылаться на сам интеллектуальный указатель.
Ответ №5:
operator->
, за которым следует вызов функции, приведет к тому, что возвращаемый указатель будет разыменован, а функция будет вызвана для результирующего объекта. Короче говоря, a->foo()
это сокращение от (*a).foo()
.
Помимо правильного ответа, который a->foo()
требует a
указывать на класс, определяющий foo
, надуманный пример может рассказать что-то другое:
struct xC {
struct Member {
void foo(){printf("xC::Member::foon");};
};
Member a;
Member* operator->() { return amp;a; }
// following function doesn't really make sense.
void foo() { printf("xC::foon");};
};
int main(){
xC x;
x.foo(); // prints "xC::foo".
x->foo();// prints "xC::Member::foo"
}
В контексте интеллектуальных указателей и итераторов stl вы часто будете видеть такого рода operator->
определения. В этом контексте определение действительно следует рекомендациям Страуструпа не злоупотреблять им для чего-то нелогичного, как в моем примере, но сделать определяющий класс пригодным для использования, как если бы он «указывал» на некоторый объект.
Комментарии:
1. 1 Хороший ответ. Я написал аналогичный пример для комментариев, которые я сделал по другим ответам : ideone.com/7tZdS
Ответ №6:
Предполагая, что a
это указатель на некоторый объект, тогда:
Это:
a->height();
Это просто сокращение для:
(*a).height();
Это позволяет вызывать метод через указатель, а не просто обычный объект (или ссылку).
На самом деле вам не нужно использовать -> version, но когда вы соединяете пару вызовов вместе, становится очевидно, почему это полезно.
person->body()->arm(right)->hand()->finger(index)->ring()->activate();
Если вы напишете эту длинную строку, это:
(*(*(*(*(*(*person).body()).arm(right)).hand()).finger(index)).ring()).activate();
Сложно соотнести отмену ссылки ( *
) с указателем, на который она отменяет ссылку. В то время как в первой версии -> объект слева — это указатель, объект справа — часть объекта после удаления ссылки на указатель.
Комментарии:
1. Полезно для того, чтобы заставить людей, придерживающихся Закона Деметры, плакать.
2. Вы имеете в виду, что класс Person должен иметь метод:
activeRing(enum LeftRight, enum Finger)
. Но спасибо за ссылку. Теперь у меня есть реальное именованное правило, которое я могу использовать для борьбы с чрезмерным использованием функций get () / set ().3. да, идея в этом: вместо того, чтобы предоставлять доступ к объекту компонента, включите весь или часть интерфейса компонента в свой. Закон также разрешает вызовы методов для создаваемых вами объектов, поэтому я думаю, что приемлемой альтернативой (хотя, по-видимому, немного бессмысленной в данном конкретном случае) было бы
person->createRingActivator(right, index).activate()
.
Ответ №7:
Как уже говорили другие, если вы говорите о разыменовании указателя, то разница не будет очень существенной. Однако, что более важно, если a
это экземпляр или ссылка и в нем определена operator->
функция, то вы можете увидеть совсем другое поведение. a.name()
вызывал бы name()
функцию a, в то время как a->name()
мог бы вызывать name()
функцию какого-либо другого типа наряду с любой другой работой, которую решил выполнить класс (это вызов функции, возвращающий указатель, поэтому класс ‘evil’ может делать практически все, что захочет, пока он возвращает действительный указатель). В основном это используется для интеллектуальных указателей, но в языке нет гарантии этого.
Ответ №8:
Все сводится к тому, насколько читаемым вы хотите видеть сам код и насколько быстрым вы хотите, чтобы он был. На самом низком уровне в ассемблере все сводится к нажатию / вытягиванию в стеке. В сегодняшней реальной жизни с быстрыми компьютерами / MCU это не имеет значения. Это стиль программирования, который вы используете. Оба способа хороши, но не смешивайте их. Это может затруднить чтение для других программистов.
Комментарии:
1.-1 НЕВЕРНО
x->y
==(*x).y
2. Указатель указывает на функцию. Ссылка — это фактический адрес функции. Посмотрите на сгенерированный код на ассемблере.
3. Да, и вы не можете обращаться к одному из них с помощью синтаксиса другого, следовательно, -1.