Есть ли преимущества передачи по указателю по сравнению с передачей по ссылке в C ++? - PullRequest
210 голосов
/ 02 декабря 2008

Каковы преимущества передачи по указателю по сравнению с передачей по ссылке в C ++?

В последнее время я видел несколько примеров, в которых аргументы функции передаются по указателям, а не по ссылке. Есть ли преимущества для этого?

Пример:

func(SPRITE *x);

с вызовом

func(&mySprite);

против

func(SPRITE &x);

с вызовом

func(mySprite);

Ответы [ 6 ]

231 голосов
/ 02 декабря 2008

Передача по указателю

  • Абонент должен взять адрес -> не прозрачный
  • Значение 0 может означать nothing. Это можно использовать для предоставления необязательных аргументов.

Передать по ссылке

  • Абонент просто передает объект -> прозрачный. Должен использоваться для перегрузки операторов, поскольку перегрузка для типов указателей невозможна (указатели являются встроенными типами). Так что вы не можете сделать string s = &str1 + &str2; с помощью указателей.
  • 0 значений невозможно -> Вызываемая функция не должна проверять их
  • Ссылка на const также принимает временные значения: void f(const T& t); ... f(T(a, b, c));, указатели не могут использоваться таким образом, поскольку вы не можете получить адрес временного.
  • Наконец, что не менее важно, ссылки легче использовать -> меньше шансов для ошибок.
207 голосов
/ 02 декабря 2008

Указатель может получить параметр NULL, а ссылочный параметр - нет. Если есть вероятность, что вы захотите передать «нет объекта», используйте указатель вместо ссылки.

Кроме того, передача по указателю позволяет вам явно видеть на сайте вызова, передан ли объект по значению или по ссылке:

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);
60 голосов
/ 02 декабря 2008

В книге Аллена Холюба «Достаточно веревки, чтобы выстрелить себе в ногу» перечислены 2 правила:

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

Он перечисляет несколько причин, по которым ссылки были добавлены в C ++:

  • они необходимы для определения конструкторов копирования
  • они необходимы для перегрузок операторов
  • const ссылки позволяют вам иметь семантику передачи по значению, избегая при этом копирования

Его главная мысль заключается в том, что ссылки не должны использоваться в качестве параметров «вывода», поскольку на сайте вызова нет указаний на то, является ли параметр ссылкой или параметром-значением. Таким образом, его правило - использовать только const ссылки в качестве аргументов.

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

58 голосов
/ 06 октября 2015

Мне нравится рассуждение по статье из "cplusplus.com:"

  1. Передача по значению, когда функция не хочет изменять параметр и значение легко копировать (целые, двойные, char, bool и т. Д. ... простые типы. Std :: string, std :: vector и все остальные контейнеры STL НЕ являются простыми типами.)

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

  3. Передача по неконстантному указателю, когда значение копируется слишком дорого, и функция хочет изменить значение, указанное на AND. NULL является допустимым, ожидаемым значением, которое обрабатывает функция.

  4. Передача по константной ссылке, когда копировать значение дорого И функция не хочет изменять значение, на которое ссылается AND NULL, не будет допустимым значением, если вместо этого используется указатель.

  5. Передача по неконтролируемой ссылке, когда значение дорого копировать, И функция хочет изменить значение, указанное в. И NULL не будет допустимым значением, если вместо этого используется указатель.

  6. При написании шаблонных функций нет однозначного ответа, потому что есть несколько компромиссов, которые следует рассмотреть, которые выходят за рамки этого обсуждения, но достаточно сказать, что большинство шаблонных функций принимают свои параметры значение или (const) ссылка, однако, поскольку синтаксис итератора подобен синтаксису указателей (от звездочки до «разыменования»), любая шаблонная функция, которая ожидает итераторы в качестве аргументов, также по умолчанию также принимает указатели (и не проверяет NULL, поскольку NULL концепция итератора имеет другой синтаксис).

http://www.cplusplus.com/articles/z6vU7k9E/

Из этого я понимаю, что основное различие между выбором указателя или ссылочного параметра заключается в том, что NULL является приемлемым значением. Вот и все.

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

8 голосов
/ 03 декабря 2008

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


Ссылки НЕ гарантия получения ненулевого указателя. (Хотя мы часто к ним относимся.)

В то время как ужасно плохой код, например, выводит вас из-под дерева плохой код, следующее скомпилирует и запустит: (По крайней мере, под моим компилятором.)

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};

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

Внезапно возникает огромная разница между foo (бар BAR) и foo (BAR & bar) . (Автоматическая побитовая операция копирования вызывается. Удаление в деструкторе вызывается дважды.)

К счастью, современные компиляторы подберут это двойное освобождение одного и того же указателя. 15 лет назад они этого не сделали. (В gcc / g ++ используйте setenv MALLOC_CHECK_ 0 , чтобы вернуться к старым способам.) В результате в DEC UNIX в одной и той же памяти выделяется два разных объекта. Там много веселья для отладки ...


Практически:

  • Ссылки скрывают, что вы меняете данные, хранящиеся в другом месте.
  • Легко спутать ссылку со скопированным объектом.
  • Указатели делают это очевидным!
3 голосов
/ 02 декабря 2008

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

Передача по ссылке, однако, имеет одно преимущество. Вы гарантированно получите экземпляр любого объекта / типа, который передается. Если вы передадите указатель, то вы рискуете получить указатель NULL. Используя передачу по ссылке, вы отправляете неявную проверку NULL на один уровень вызывающей стороне вашей функции.

...