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

StackOverflow https://stackoverflow.com/questions/334856

Вопрос

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

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

Пример:

func(SPRITE *x);

с вызовом

func(&mySprite);

против.

func(SPRITE &x);

с вызовом

func(mySprite);
Это было полезно?

Решение

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

Другие советы

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

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

Перейти по ссылке

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

Настоящая проблема, с которой я сталкиваюсь со ссылками, связана с другими программистами, которых в дальнейшем называют ИДИОТЫ, которые выделяют в конструкторе, освобождают в деструкторе, и не предоставить конструктор копирования или оператор =().

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

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


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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top