Есть ли преимущества перехода по указателю на передачу по ссылке в С++?

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

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

Пример:

func(SPRITE *x);

с вызовом

func(&mySprite);

против.

func(SPRITE &x);

с вызовом

func(mySprite);

Ответ 1

Указатель может принимать параметр 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);

Ответ 2

Передача указателем

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

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

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

Ответ 3

Аллен Голуб "Достаточно веревки, чтобы стрелять себе в ногу" перечислены следующие 2 правила:

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

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

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

Его основная точка зрения заключается в том, что ссылки не должны использоваться как "выходные" параметры, потому что на сайте вызова нет указания на то, является ли параметр опорным или параметром значения. Поэтому его правило состоит только в использовании ссылок const в качестве аргументов.

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

Ответ 4

Мне нравится аргументация из статьи cplusplus.com:

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

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

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

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

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

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

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

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

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

Ответ 5

Разъяснения к предыдущим сообщениям:


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

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

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

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

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

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

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


Более практично:

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

Ответ 6

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

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