Функции и операторы
Если ваша процедура имеет десять параметров — вероятно, вы где-то ошиблись.
Функции, включая перегруженные операторы, представляют собой фундаментальные единицы работы. Как вы увидите позже в разделе 'Обработка ошибок и исключения' (в частности, в рекомендации 70), это непосредственно влияет на наши рассуждения о корректности и безопасности кода.
Но давайте сначала рассмотрим некоторые фундаментальные вопросы написания функций, в том числе операторов. В частности, мы обратим особое внимание на их параметры, семантику и перегрузку.
В этом разделе наиболее важной нам представляется рекомендация 26 — 'Сохраняйте естественную семантику перегруженных операторов'.
25. Передача параметров по значению, (интеллектуальному) указателю или ссылке
Вы должны четко уяснить разницу между входными, выходными параметрами и параметрами, предназначенными и для ввода, и для вывода информации, а также между передачей параметров по значению и по ссылке, и корректно их использовать.
Правильный выбор способа передачи аргументов в функцию — по значению, ссылке или с использованием указателей — весьма необходимый навык, который поможет вам сделать ваш код максимально безопасным и эффективным.
Хотя эффективность не должна быть нашей главной целью (см. рекомендацию 8), при прочих равных условиях, включая понятность кода, мы не должны без крайней необходимости снижать его эффективность (см. рекомендацию 9).
Вам стоит следовать приведенным ниже рекомендациям при выборе способа передачи параметров. Вот рекомендации для входных параметров (которые передают информацию в функцию, но не возвращают ее).
• Всегда описывайте все указатели или ссылки на входные параметры как const
.
• Предпочитайте передачу примитивных типов (например, char
или float
) и объектов-значений с недорогими операциями копирования (например, Point
или Complex<float>
) по значению.
• Входные аргументы прочих пользовательских типов лучше передавать как ссылки на const
.
• Подумайте о передаче по значению вместо передачи по ссылке, если функция требует создания копии аргумента. Концептуально передача по значению эквивалентна передаче ссылки на константный объект и выполнение копирования, и может помочь компилятору в выполнении оптимизации по устранению временных переменных.
Далее приведены рекомендации для выходных параметров (а также параметров для одновременного ввода и вывода информации).
• Если аргумент необязателен, лучше передать его как (интеллектуальный) указатель (что позволит вызывающей функции передать нулевое значение как указание, что аргумент отсутствует); тот же совет применим и в случае, если функция сохраняет копию указателя или как-то иначе работает с принадлежностью аргумента.
• Если аргумент обязательный и функция не сохраняет указатель на него или каким-то иным образом влияет на его принадлежность, то такой аргумент лучше передавать по ссылке. Это указывает, что наличие данного аргумента обязательно, и заставляет вызывающую функцию отвечать за предоставление корректного объекта.
Не используйте функции с переменным количеством аргументов (см. рекомендацию 98).
26. Сохраняйте естественную семантику перегруженных операторов
Программисты ненавидят сюрпризы. Перегружайте операторы только в случае веских на то оснований, и сохраняйте при этом их естественную семантику. Если это оказывается сложным, возможно, вы неверно используете перегрузку операторов.
Хотя все (как мы надеемся) согласны с тем, что не следует реализовывать вычитание как оператор operator+
, прочие ситуации не столь очевидны. Например, означает ли оператор operator*
вашего класса Tensor
скалярное или векторное умножение? Должен ли оператор operator+=(Tensor& t, unsigned u)
прибавлять u
к каждому из элементов t
, или должен изменять размер t
? В таких неоднозначных или не поддающихся интуитивному пониманию случаях следует использовать именованные функции, а не прибегать к шифрованию.
Для типов-значений (но не для всех типов; см. рекомендацию 32) следует придерживаться правила: 'Если не знаешь, как поступить — поступай так, как int
' [Meyers96]. Подражание поведению операторов встроенных типов и взаимоотношениям между ними гарантирует, что вы никого не приведете в замешательство. Если выбранная вами семантика заставляет кого-то удивленно поднять брови, может быть, перегрузка оператора — не самая лучшая идея?
Программисты ожидают, что операторы идут в связке — если выражение a@b
имеет определенный смысл для некоторого определенного вами оператора @
(возможно, после преобразования типов), то задайте сами себе вопрос: можно ли написать b@a
без неприятных последствий? Можно ли написать a@=b
? (См. рекомендацию 27.) Если оператор имеет обратный оператор (например, +
и -
, *
и /
), то поддерживаются ли они оба?
От именованных функций не ожидается наличие соответствующих взаимоотношений, так что для большей ясности кода, если возможно неверное истолкование семантики перегруженных операторов, лучше использовать именно функции.
Имеются высокоспециализированные библиотеки (например, генераторы синтаксических анализаторов), в предметной области которых соглашения о семантике операторов существенно