С++: использование '.' оператор над выражениями и вызовами функций

Мне было интересно, хорошо ли использовать оператор-член . следующим образом:

someVector = (segment.getFirst() - segment.getSecond()).normalize().normalCCW();

Просто сделал это, чтобы показать две разные вещи, которые меня интересовали, а именно, соответствует ли использование (expressions).member/function() и foo.getBar().getmoreBar() духу удобочитаемости и ремонтопригодности. Во всем коде C++ и книгах, которые я изучил, я никогда не видел, чтобы он использовался таким образом, но его опьяняюще легко использовать как таковой. Однако не хочется развивать какие-либо вредные привычки.

Вероятно, это более (или менее) важно, мне также было интересно, будет ли какой-либо прирост / потеря производительности при использовании его таким образом или непредвиденные ловушки, которые могут привести к ошибкам в программе.

Заранее спасибо!


person Anne Quinn    schedule 21.02.2011    source источник
comment
var является самостоятельным выражением. var.member вдвойне. Так что по этой логике...   -  person    schedule 21.02.2011
comment
См. соответствующий раздел: Является ли этот код корректным?   -  person Nawaz    schedule 21.02.2011


Ответы (8)


или непредвиденные подводные камни, которые могут внести ошибки в программу

Ну и возможные подводные камни

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

  2. Трудно читать (иногда). Цепочка вызовов функций может затруднить чтение кода. Это зависит от ситуации, здесь нет жесткого правила. Если выражение даже несколько сложное, оно может затруднить понимание. У меня нет проблем с чтением вашего конкретного примера.

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

person Ed S.    schedule 21.02.2011
comment
Спасибо! Тогда мне придется избегать использования его с чем-либо более сложным, чем пример. Я иногда забываю, что многие функции возвращают мусор в случае сбоя, особенно те, которые возвращают указатели. К счастью, мой отладчик вводит любые вызовы функций в оператор, если я ему скажу, так что, по крайней мере, это не должно быть большой проблемой. - person Anne Quinn; 21.02.2011

Да, это вполне приемлемо и на самом деле было бы совершенно нечитаемым во многих контекстах, если бы вы НЕ делали этого.

Это называется цепочкой методов.

МОЖЕТ быть некоторый выигрыш в производительности, если вы не создаете временные переменные. Но любой грамотный компилятор все равно его оптимизирует.

person Falmarri    schedule 21.02.2011
comment
Кроме того, это также может сделать код полностью нечитаемым во многих контекстах. И имеет потенциал (в зависимости от того, как это реализовано) ввести некоторые ошибки области видимости. Как и большинство вещей, это действительно зависит от того, как вы его используете: если это делает его более читабельным, дерзайте; если это делает его менее читаемым, не делайте этого. - person Zac Howland; 21.02.2011
comment
Мне было интересно, есть ли у этого имя! И мне нравилось не полагаться на временные переменные, когда нужно было только разбить выражение. (Хотя я все еще использую их для кэширования результатов) @Zac: Судя по большинству ответов, это похоже на консенсус. Я иногда забываю, что программирование включает в себя подобные суждения. Я должен быть уверен, что с этого момента и далее буду думать только о том, что вы сказали в последний раз, спасибо! - person Anne Quinn; 21.02.2011

совершенно правильно использовать его так, как вы показали. Он используется в идиоме именованных параметров, описанной в C++. faq lite например.

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

my2c

person neuro    schedule 21.02.2011
comment
Я только что закончил читать ту статью, на которую вы ссылаетесь. Это дает мне довольно много идей для некоторых проблемных классов, которые у меня есть. Спасибо! - person Anne Quinn; 21.02.2011
comment
@Clairvoire Добро пожаловать. C++ faq lite необходимо прочитать, если вы преодолели базовые навыки C++. Удачи с кодом :) - person neuro; 22.02.2011

Использование переменной для хранения промежуточных результатов иногда может улучшить читаемость, особенно если вы используете хорошие имена переменных. Чрезмерная цепочка может затруднить понимание того, что происходит. Вы должны использовать свое суждение, чтобы решить, стоит ли разбивать цепочки с использованием переменных. Пример, который вы привели выше, не является для меня чрезмерным. Производительность не должна сильно отличаться в ту или иную сторону, если вы включите оптимизацию.

person Emile Cormier    schedule 21.02.2011
comment
Спасибо! Как ни странно, я обнаружил, что почти 50% своего времени при написании кода трачу на то, чтобы придумать хорошие имена для переменных/классов/функций. Однако всегда терпите неудачу, когда дело доходит до временных переменных, и в конечном итоге сдаетесь и называете это «double temp_double» или что-то столь же ленивое. Тогда я постараюсь избегать использования чрезмерных цепочек. - person Anne Quinn; 21.02.2011
comment
Тезаурус или другие библиотеки иногда могут дать вам вдохновение для хороших имен переменных. Если вы потратите время на придумывание кратких и понятных имен, вы сэкономите время позже при документировании кода. Код уже будет понятен. - person Emile Cormier; 22.02.2011

someVector = (segment.getFirst() - segment.getSecond()).normalize().normalCCW();

Не ответ на ваш вопрос, но я должен сказать вам, что поведение выражения (segment.getFirst() - segment.getSecond()) не определено четко в соответствии со стандартом C++. Порядок, в котором вычисляется каждый операнд, не определен стандартом!

Также см. этот связанный раздел: Является ли этот код корректным?

person Nawaz    schedule 21.02.2011
comment
Ой, я думаю, мы можем предположить, что getFirst() и getSecond() возвращают вектор, который, например, перегружен. - person Anne Quinn; 21.02.2011
comment
@Clairvoire: Это имеет смысл. Мой пост следует принять в качестве меры предосторожности. Ведь я не пытался ответить! - person Nawaz; 21.02.2011
comment
Хорошо! И спасибо за эту ссылку. Я очень рад, что делать что-либо, что было изобретено с помощью этой цепочки методов, никогда не придет мне в голову. - person Anne Quinn; 21.02.2011
comment
Что не точно определено? Предполагая, что getFirst() и getSecond() являются константными функциями (что, я думаю, является безопасным предположением), результат совершенно точно определен. Если вы имеете в виду порядок, в котором вызываются две функции, вы правы, но я думаю, что это не имеет отношения к расчету. - person Benjamin Lindley; 21.02.2011

Я предполагаю, что то, что вы делаете, менее читабельно, однако, с другой стороны, слишком много временных переменных также могут стать нечитаемыми.

Что касается производительности, я уверен, что при создании временных переменных возникают небольшие накладные расходы, но компилятор может это оптимизировать.

person Marlon    schedule 21.02.2011

Нет большой проблемы с его использованием таким образом — некоторые API значительно выигрывают от цепочки методов. Кроме того, неправильно создавать переменную, а затем использовать ее только один раз. Когда кто-то читает следующую строку, ему не нужно думать обо всех тех переменных, которые вы сейчас не сохранили.

person Puppy    schedule 21.02.2011
comment
Собственно, теперь, когда вы упомянули об этом. Мне придется пересмотреть, как я использовал некоторые библиотеки, которые у меня есть, просто чтобы посмотреть, будет ли это работать с ними лучше. Спасибо! - person Anne Quinn; 21.02.2011

Это зависит от того, что вы делаете.

Для удобочитаемости попробуйте использовать промежуточные переменные.
Назначьте результаты вычислений указателям, а затем используйте их.

person Yochai Timmer    schedule 21.02.2011
comment
В основном так я научился это делать. Но я не уверен, что следую тому, для чего нужен указатель... - person Anne Quinn; 21.02.2011
comment
Вы хотите сохранить результат... Итак, если бы вы использовали не указатель, а переменную класса, она вызвала бы конструктор копирования и скопировала данные. Пустая трата времени. - person Yochai Timmer; 21.02.2011
comment
Не могли бы вы показать пример того, что вы предлагаете? Как присвоить результат вычисления указателю, если функция не возвращает указатель? - person Benjamin Lindley; 21.02.2011