Передача модифицируемого параметра в функцию c ++ - PullRequest
7 голосов
/ 24 августа 2009

При условии, что я хочу передать изменяемый параметр в функцию, что мне выбрать: передать его по указателю или передать по ссылке?

  1. bool GetFoo (Foo & whereToPlaceResult);
  2. bool GetFoo (Foo * whereToPlaceResult);

Я спрашиваю об этом, потому что я всегда считал, что лучше всего передавать параметр по ссылке (1), но после изучения некоторой локальной базы данных кода я пришел к выводу, что наиболее распространенным является (2). Более того, сам человек (Бьярне Страуструп) рекомендует использовать (2). В чем заключаются преимущества (1) и (2) или это просто вопрос личного вкуса?

Ответы [ 12 ]

22 голосов
/ 24 августа 2009

Я предпочитаю ссылку вместо указателя, когда:

  • Это не может быть нулем
  • Нельзя изменить (указать на что-то еще)
  • Он не должен быть удален (тем, кто получает указатель)

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

Лично я предпочитаю ссылку по следующей причине:

  1. Я думаю, что подпрограмма должна знать, какую подпрограмму она вызывает
  2. Подпрограмма не должна предполагать, из какой подпрограммы она вызывается.

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

[2.] Подразумевает, что если это указатель, то подпрограмма должна обрабатывать возможность того, что параметр является нулевым указателем, который может быть лишним и бесполезным кодом IMO.

Более того, всякий раз, когда я вижу указатель, я думаю: «кто и когда удалит это?», Поэтому, когда / где владение / срок действия / удаление не является проблемой, я предпочитаю использовать ссылку.

Для чего бы это ни стоило, я имею привычку писать const-правильный код: поэтому, если я заявляю, что метод имеет неконстантный ссылочный параметр, тот факт, что он неконстантный, имеет значение. Если бы люди не писали код с правильной константой, то, возможно, было бы сложнее сказать, будет ли параметр изменен в подпрограмме, и аргумент для другого механизма (например, указатель вместо ссылки) будет немного сильнее.

5 голосов
/ 24 августа 2009

Преимущества прохождения по ссылке:

  • Заставляет пользователя указывать значение.
  • Менее подвержен ошибкам: обрабатывает разыменование указателя самостоятельно. Не нужно проверять наличие нуля внутри.
  • делает вызывающий код более чистым.

Преимущества передачи указателя по значению:

  • Позволяет передать значение null для "необязательных" параметров. Вроде мерзкий взлом, но иногда полезный.
  • Заставляет вызывающего абонента знать, что делается с параметром.
  • Дает читателю половину понятия о том, что может быть сделано с параметром без необходимости читать API.

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

Действительно, передача указателя - просто подверженный ошибкам грязный хакерский остаток от C, у которого не было другого способа передать значения по ссылке. У C ++ есть способ, поэтому взлом больше не нужен.

4 голосов
/ 24 августа 2009

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

3 голосов
/ 24 августа 2009

Я бы порекомендовал вам рассмотреть (может быть не лучшим для каждой ситуации) возвращать Foo из функции, а не изменять параметр. Ваш прототип функции будет выглядеть так:

Foo GetFoo() // const (if a member function)

Поскольку вы, кажется, возвращаете флаг успеха / неудачи, лучше использовать стратегию исключения.

Преимущества:

  • Вы избегаете всех проблем с указателями / ссылками
  • Упрощает жизнь для звонящего. Может передавать возвращаемое значение другим функциям без использования локальной переменной, например.
  • Вызывающий не может игнорировать состояние ошибки, если вы выбросили исключение.
  • Оптимизация возвращаемого значения означает, что она может быть столь же эффективной, как и изменение параметра.
1 голос
/ 24 августа 2009

Передайте по ссылке и избегайте всей проблемы с указателем NULL.

1 голос
/ 24 августа 2009

Я выбрал # 2, потому что в момент вызова очевидно, что параметр будет изменен.

GetFoo (& var) вместо GetFoo (var)

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

0 голосов
/ 24 августа 2009

Мои предпочтения - это ссылки. Во-первых, потому что это рифмуется. :) Также из-за проблем, указанных в других ответах: нет необходимости разыменования, и нет возможности ссылки на NULL. Другая причина, о которой я не упомянул, заключается в том, что когда вы видите указатель, вы не можете быть уверены, указывает ли он на динамически распределенную память, и у вас может возникнуть желание вызвать delete для него. Ссылка, с другой стороны, не допускает никакой двусмысленности в отношении управления памятью.

Сказав это, конечно, во многих случаях передача указателя предпочтительнее или даже необходима. Если вы заранее знаете, что этот параметр является необязательным, то использование значения NULL очень полезно. Точно так же вы можете заранее знать, что параметр всегда динамически распределяется и все управление памятью отработано.

0 голосов
/ 24 августа 2009

В эти дни я использую константные ссылки для входных параметров и указатели для выходных параметров. FWIIW, Google C ++ Style Guide рекомендует тот же подход (не то, чтобы я всегда соглашался с их руководством по стилю - например, они не используют исключения, что обычно не имеет особого смысла)

0 голосов
/ 24 августа 2009

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

Ссылки в противном случае более безопасны, если у вас есть такой, который гарантированно существует и доступен для записи (если, конечно, не const)

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

0 голосов
/ 24 августа 2009

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

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