Когда использовать ссылки против указателей


381

Я понимаю синтаксис и общую семантику указателей по сравнению со ссылками, но как мне решить, когда более или менее целесообразно использовать ссылки или указатели в API?

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

Например, в следующем коде:

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

С указателем всегда (более) очевидно, что происходит, поэтому для API и тому подобного, где ясность представляет большую проблему, указатели не более уместны, чем ссылки? Означает ли это, что ссылки должны использоваться только при необходимости (например operator++)? Есть ли проблемы с производительностью с одним или другим?

РЕДАКТИРОВАТЬ (УДАЛЕНО):

Помимо разрешения значений NULL и работы с необработанными массивами, кажется, что выбор сводится к личным предпочтениям. Я согласился с ответом ниже, который ссылается на Руководство по стилю Google C ++ , поскольку в нем представлено мнение, что «Ссылки могут сбивать с толку, поскольку они имеют синтаксис значений, но семантику указателей».

Из-за дополнительной работы, необходимой для очистки аргументов указателя, которые не должны быть равны NULL (например add_one(0), вызовет версию указателя и прервется во время выполнения), имеет смысл с точки зрения сопровождения использовать ссылки, где ДОЛЖЕН присутствовать объект, хотя это позор. потерять синтаксическую ясность.


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

1
@Schnommus: Достаточно справедливо, я в основном использую TextMate. Тем не менее, я думаю, что лучше, чтобы смысл был очевиден с первого взгляда.
connect

4
Как насчет add_one(a);неясно, что aбудет изменено? Это говорит прямо в коде: добавьте один .
GManNickG

32
@connec: Руководство по стилю Google C ++ не считается хорошим руководством по стилю C ++. Это руководство по стилю для работы со старой базой кода Google C ++ (т.е. хорошо для своих вещей). Принятие ответа, основанного на этом, никому не помогает. Просто читая ваши комментарии и объяснения, вы пришли к этому вопросу с уже высказанным мнением и просто ищете других людей, чтобы подтвердить вашу точку зрения. В результате вы основываете вопрос и отвечаете на то, что вы хотите / ожидаете услышать.
Мартин Йорк,

1
Это просто исправляется путем присвоения имени методу addOneTo(...). Если это не то, что вы хотите сделать, просто посмотрите на объявление.
Стефан

Ответы:


297

Используйте ссылки, где вы можете, указатели, где вы должны.

Избегайте указателей, пока не сможете.

Причина в том, что указатели затрудняют чтение / чтение, делают их менее безопасными и намного более опасными манипуляциями, чем любые другие конструкции.

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

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

Другой пример - использование указателей на необработанную память для определенных манипуляций с памятью. Это должно быть скрыто и локализовано в очень узких частях кода, чтобы помочь ограничить опасные части всей кодовой базы.

В вашем примере нет смысла использовать указатель в качестве аргумента, потому что:

  1. если вы предоставите nullptrв качестве аргумента, вы попадете в неопределенную область поведения;
  2. версия справочного атрибута не позволяет (без простых уловок) проблему с 1.
  3. Ссылочная версия атрибута проще для пользователя: вам нужно предоставить действительный объект, а не то, что может быть нулевым.

Если поведение функции должно работать с заданным объектом или без него, то использование указателя в качестве атрибута предполагает, что вы можете передать его nullptrв качестве аргумента, и это нормально для функции. Это своего рода контракт между пользователем и реализацией.


49
Я не уверен, что указатели затрудняют чтение? Это довольно простая концепция, которая дает понять, когда что-то может быть изменено. Если что-то скажу, что труднее читать, когда нет признаков того, что происходит, почему бы add_one(a)не вернуть результат, а не установить его по ссылке?
connect

46
@connec: Если add_one(a)сбивает с толку, то это потому, что он неправильно назван. add_one(&a)будет иметь такую ​​же путаницу, только теперь вы можете увеличивать указатель, а не объект. add_one_inplace(a)позволит избежать путаницы.
Николь Болас

20
Одно замечание: ссылки могут относиться к памяти, которая может исчезнуть так же легко, как указатели. Таким образом, они не обязательно безопаснее указателей. Сохранение и передача ссылок могут быть такими же опасными, как указатели.
Дуг Т.

6
@Klaim Я имел в виду сырые указатели. Я имел в виду , что C ++ имеет указатели, NULLи nullptr, и он имеет их по причине. И это не продуманный и даже не реальный совет давать «никогда не используйте указатели» и / или «никогда не используйте NULL, всегда используйте boost::optional». Это просто безумие. Не поймите меня неправильно, необработанные указатели нужны в C ++ реже, чем в C, но, тем не менее, они полезны, они не настолько «опасны», как любят утверждать некоторые люди C ++ (это тоже преувеличение), и снова: когда проще просто использовать указатель и return nullptr;указать отсутствующее значение ... Зачем импортировать весь Boost?

5
@ Klaim «использование NULL - плохая практика» - теперь это просто смешно. И ifустарел, и нужно использовать while() { break; }вместо этого, верно? Кроме того, не волнуйтесь, я видел и работал с большими базами кода, и да, если вы неосторожны , то владение является проблемой. Но если вы придерживаетесь соглашений, используйте их последовательно, комментируйте и документируйте свой код. Но в конце концов, я должен просто использовать C, потому что я слишком туп для C ++, верно?

62

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

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

Некоторые соглашения по кодированию (например, Google ) предписывают, что всегда следует использовать указатели или константные ссылки, потому что ссылки имеют немного неясный синтаксис: они имеют ссылочное поведение, но синтаксис значения.


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

44
@Dan: Руководство по стилю Google предназначено для старого кода Google и не должно использоваться для современного кодирования. На самом деле, это довольно плохой стиль кодирования для нового проекта.
GManNickG

13
@connec: Позвольте мне выразиться так: null - это абсолютно правильное значение указателя. Везде, где есть указатель, я могу дать ему значение null. Ergo вашего второго варианта add_oneбудет сломано : add_one(0); // passing a perfectly valid pointer value, Kaboom. Вы должны проверить, если это ноль. Некоторые люди возразят: «Хорошо, я просто задокументирую, что моя функция не работает с нулем». Это нормально, но тогда вы проигнорируете цель вопроса: если вы посмотрите документацию, чтобы увидеть, в порядке ли null, вы также увидите объявление функции .
GManNickG

8
Если бы это была ссылка, вы бы это увидели. Однако такая реплика упускает суть: на уровне языка ссылки навязывают существующий объект и, возможно, не имеют значения null, тогда как указатели не имеют такого ограничения. Я думаю, что очевидно, что принудительное применение на уровне языка более эффективно и менее подвержено ошибкам, чем принудительное применение на уровне документации. Некоторые попытаются повторить это, сказав: «Смотри, нулевая ссылка: int& i = *((int*)0);это неверная реплика. Проблема в предыдущем коде связана с использованием указателя, а не ссылки . Ссылки никогда не бывают нулевыми, точка.
GManNickG

12
Здравствуйте, я заметил недостаток языковых юристов в комментариях, так что позвольте мне исправить: ссылки обычно используются указателями, но стандарт не говорит об этом. Реализация, использующая какой-то другой механизм, была бы 100% жалобой.
Томас Бонини

34

Из C ++ FAQ Lite -

Используйте ссылки, когда можете, и указатели, когда нужно.

Ссылки обычно предпочтительнее указателей, когда вам не нужно «перезаписывать». Обычно это означает, что ссылки наиболее полезны в открытом интерфейсе класса. Ссылки обычно появляются на коже объекта, а указатели на внутренней стороне.

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

Примечание. Программистам старой строки C иногда не нравятся ссылки, поскольку они предоставляют семантику ссылок, которая не указана в коде вызывающей стороны. Однако, после некоторого опыта в C ++, быстро понимаешь, что это форма сокрытия информации, которая является активом, а не пассивом. Например, программисты должны писать код на языке проблемы, а не на языке машины.


1
Я думаю, вы могли бы поспорить, что если вы используете API, вы должны быть знакомы с тем, что он делает, и знать, изменен ли переданный параметр или нет ... что-то рассмотреть, но я согласен с программистами C ( хотя я немного C опыт сам). Я бы добавил, что этот более понятный синтаксис полезен как программистам, так и машинам.
connect

1
@connec: Уверен, что программист на C правильно для своего языка. Но не делайте ошибку, рассматривая C ++ как C. Это совершенно другой язык. Если вы рассматриваете C ++ как C, вы в конечном итоге пишете то, на что ссылаются равным образом C with class(что не является C ++).
Мартин Йорк,

15

Мое правило:

  • Используйте указатели для исходящих или входящих / исходящих параметров. Таким образом, можно увидеть, что значение будет изменено. (Вы должны использовать &)
  • Используйте указатели, если параметр NULL является допустимым значением. (Убедитесь, что это constвходной параметр)
  • Используйте ссылки для входящего параметра, если он не может быть NULL и не является примитивным типом ( const T&).
  • Используйте указатели или умные указатели при возврате вновь созданного объекта.
  • Используйте указатели или умные указатели в качестве членов структуры или класса вместо ссылок.
  • Используйте ссылки для псевдонимов (например. int &current = someArray[i])

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


14

Отказ от ответственности: кроме того факта, что ссылки не могут быть NULL или «отскок» (то есть они не могут изменить объект, псевдоним которого они имеют), это действительно сводится к вопросу вкуса, поэтому я не собираюсь говорить "это лучше".

Тем не менее, я не согласен с вашим последним утверждением в посте, так как я не думаю, что код теряет ясность со ссылками. В вашем примере

add_one(&a);

может быть яснее, чем

add_one(a);

так как вы знаете, что, скорее всего, значение изменится. С другой стороны, хотя подпись функции

void add_one(int* const n);

также не совсем понятно: будет ли n целым числом или массивом? Иногда у вас есть доступ только к (плохо документированным) заголовкам и таким подписям

foo(int* const a, int b);

не легко интерпретировать с первого взгляда.

Имхо, ссылки так же хороши, как указатели, когда не требуется (пере) перераспределение или повторное связывание (в смысле, объясненном ранее). Более того, если разработчик использует только указатели для массивов, сигнатуры функций несколько менее неоднозначны. Не говоря уже о том, что синтаксис операторов гораздо удобнее читать по ссылкам.


Спасибо за четкую демонстрацию того, где оба решения приобретают и теряют ясность. Первоначально я был в лагере указателей, но это имеет большой смысл.
Зак Бивон-Коллин

12

Как и другие уже ответили: ссылки всегда использовать, если переменная является NULL/ nullptrне является на самом деле действительным состоянием.

Точка зрения Джона Кармака на эту тему похожа:

NULL-указатели являются самой большой проблемой в C / C ++, по крайней мере, в нашем коде. Двойное использование одного значения как флага и адреса вызывает невероятное количество фатальных проблем. С ++ ссылки должны быть предпочтительнее указателей, когда это возможно; в то время как ссылка «действительно» является просто указателем, она имеет неявный контракт, не являющийся NULL. Выполняйте NULL-проверки, когда указатели превращаются в ссылки, после чего вы можете игнорировать проблему после этого.

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

Редактировать 2012-03-13

Пользователь Bret Kuhns справедливо отмечает:

Стандарт C ++ 11 был доработан. Я думаю, что пришло время упомянуть, что большая часть кода должна прекрасно работать с комбинацией ссылок, shared_ptr и unique_ptr.

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

Например, оба std::unique_ptrи std::shared_ptrмогут быть созданы как «пустые» указатели через их конструктор по умолчанию:

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

И затем, у нас есть забавная проблема "как мы передаем умный указатель в качестве параметра функции?"

Jon «сек ответ на вопрос C ++ - передача ссылки на повышение :: shared_ptr , и следующие комментарии показывают , что даже тогда, передавая смарт - указатель на копию или по ссылке не столь четкого среза , как хотелось бы (я одобряю себя в" по ссылке "по умолчанию, но я могу ошибаться).


1
Стандарт C ++ 11 был доработан. Я думаю, что пора в этой теме упомянуть, что большая часть кода должна прекрасно работать с комбинацией ссылок shared_ptr, и unique_ptr. Семантика владения и условные обозначения параметров входа / выхода обеспечиваются сочетанием этих трех частей и постоянством. Почти нет необходимости в необработанных указателях в C ++, за исключением случаев, когда речь идет об устаревшем коде и очень оптимизированных алгоритмах. Те области, где они используются, должны быть как можно более инкапсулированы и преобразовывать любые необработанные указатели в семантически подходящий «современный» эквивалент.
Брет Кунс,

1
Большую часть времени умные указатели не должны передаваться, но должны проверяться на нулевое значение, а затем содержащийся в них объект передается по ссылке. Единственный раз, когда вы действительно должны передать умный указатель, это когда вы передаете (unique_ptr) или делитесь (shared_ptr) правами собственности с другим объектом.
Люк Ворт

@povman: Я полностью согласен: если владелец не является частью интерфейса (и если он не собирается изменяться, его не должно быть), то мы не должны передавать умный указатель в качестве параметра (или возвращаемого значения). Все становится немного сложнее, когда владение является частью интерфейса. Например, Sutter / Meyers обсуждают, как передать unique_ptr в качестве параметра: копией (Sutter) или ссылкой на r-значение (Meyers)? Антипаттерн полагается на передачу указателя на глобальный shared_ptr с риском того, что этот указатель будет признан недействительным (решение заключается в копировании умного указателя в стек)
paercebal

7

Это не вопрос вкуса. Вот некоторые окончательные правила.

Если вы хотите сослаться на статически объявленную переменную в области, в которой она была объявлена, используйте ссылку C ++, и она будет совершенно безопасной. То же самое относится и к статически объявленному интеллектуальному указателю. Передача параметров по ссылке является примером такого использования.

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

Вы можете ссылаться на элемент коллекции со ссылкой для удобства синтаксиса, но это не безопасно; элемент может быть удален в любое время.

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


5

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

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

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

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

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

Здесь имя функции дает понять, что вы получаете информацию обратно в массив. Так что нет путаницы.

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


4

Скопировано из вики -

Следствием этого является то, что во многих реализациях работа с переменной с автоматическим или статическим временем жизни через ссылку, хотя синтаксически аналогична обращению к ней напрямую, может включать операции скрытой разыменования, которые являются дорогостоящими. Ссылки являются синтаксически противоречивой особенность C ++, потому что они затеняют уровень Идентификатор, в косвенности; то есть, в отличие от кода C, где указатели обычно выделяются синтаксически, в большом блоке кода C ++ может быть неочевидно, если объект, к которому осуществляется доступ, определяется как локальная или глобальная переменная или является ли он ссылкой (неявным указателем) на какое-то другое место, особенно если код смешивает ссылки и указатели. Этот аспект может затруднить чтение и отладку плохо написанного кода C ++ (см. Псевдоним).

Я согласен на 100% с этим, и поэтому я считаю, что вы должны использовать ссылку только тогда, когда у вас есть очень веские основания для этого.


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

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

3

Что нужно иметь в виду:

  1. Указатели могут быть NULL, ссылки не могут быть NULL.

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

  3. Указатель используется с указателями *while, используемыми с &.

  4. Используйте указатели, когда требуется арифметическая операция с указателем.

  5. Вы можете иметь указатели на тип void, int a=5; void *p = &a;но не можете иметь ссылку на тип void.

Указатель против ссылки

void fun(int *a)
{
    cout<<a<<'\n'; // address of a = 0x7fff79f83eac
    cout<<*a<<'\n'; // value at a = 5
    cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
    cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
    cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);

Вердикт, когда использовать что

Указатель : для массива, списка ссылок, реализации дерева и арифметики указателя.

Справка : в параметрах функции и типах возвращаемых данных.


2

Существует проблема с правилом « использовать ссылки везде, где это возможно », и оно возникает, если вы хотите сохранить ссылку для дальнейшего использования. Чтобы проиллюстрировать это на примере, представьте, что у вас есть следующие классы.

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

На первый взгляд может показаться хорошей идеей, чтобы параметр в RefPhone(const SimCard & card)конструкторе передавался по ссылке, поскольку он предотвращает передачу неверных / нулевых указателей в конструктор. Это как-то поощряет распределение переменных в стеке и использование преимуществ RAII.

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

Но затем приходят временники, чтобы разрушить ваш счастливый мир.

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

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

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


Существуют следующие различия между указателями и ссылками.

  1. Когда дело доходит до передачи переменных, передача по ссылке выглядит как передача по значению, но имеет семантику указателя (действует как указатель).
  2. Ссылка не может быть непосредственно инициализирована 0 (ноль).
  3. Ссылка (ссылка, объект без ссылки) не может быть изменена (эквивалентно указателю "* const").
  4. Ссылка const может принимать временный параметр.
  5. Локальные константные ссылки продлевают время жизни временных объектов

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

  • Используйте ссылки для параметров, которые будут использоваться локально в области действия функции.
  • Используйте указатели, когда 0 (ноль) является приемлемым значением параметра или вам нужно сохранить параметр для дальнейшего использования. Если 0 (ноль) приемлемо, я добавляю суффикс "_n" к параметру, использую защищенный указатель (как QPointer в Qt) или просто документирую его. Вы также можете использовать умные указатели. Вы должны быть еще более осторожны с общими указателями, чем с обычными указателями (в противном случае вы можете получить утечки памяти и беспорядок ответственности).

3
Проблема с вашим примером не в том, что ссылки небезопасны, а в том, что вы полагаетесь на что-то вне области действия вашего объекта, чтобы сохранить ваши частные члены живыми. const SimCard & m_card;это просто плохо написанный код.
Пламенко

@plamenko Боюсь, вы не понимаете цель примера. Является ли const SimCard & m_cardэто правильно или нет , зависит от контекста. Сообщение в этом посте не о том, что ссылки небезопасны (хотя они могут быть, если кто-то старается). Суть в том, что вы не должны слепо придерживаться заклинания «использовать ссылки, когда это возможно». Пример - это результат агрессивного использования доктрины «используйте ссылки, когда это возможно». Это должно быть ясно.
док

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

Вы практически никогда не должны иметь таких членов, как const SimCard & m_card. Если вы хотите работать с временными специалистами, добавьте explicit RefPhone(const SimCard&& card)конструктор.
Пламенко

@plamenko, если ты не умеешь читать с некоторым базовым пониманием, у тебя есть большая проблема, чем просто ввод в заблуждение моим постом. Я не знаю, как я мог бы быть более ясным. Посмотрите на первое предложение. Существует проблема с заклинанием «использовать ссылки всегда, когда это возможно»! Где в моем посте вы нашли утверждение, что ссылки плохие? В конце моего поста вы написали, где использовать ссылки, так как вы пришли к таким выводам? Это не прямой ответ на вопрос?
Док

1

Ниже приведены некоторые рекомендации.

Функция использует переданные данные без их изменения:

  1. Если объект данных небольшой, например, встроенный тип данных или небольшая структура, передайте его по значению.

  2. Если объект данных является массивом, используйте указатель, потому что это ваш единственный выбор. Сделать указатель указателем на const.

  3. Если объект данных представляет собой структуру хорошего размера, используйте константный указатель или константную ссылку для повышения эффективности программы. Вы экономите время и пространство, необходимые для копирования структуры или дизайна класса. Сделайте указатель или ссылку const.

  4. Если объект данных является объектом класса, используйте константную ссылку. Семантика дизайна класса часто требует использования ссылки, которая является основной причиной, по которой C ++ добавил эту функцию. Таким образом, стандартный способ передачи аргументов объекта класса - это ссылка.

Функция изменяет данные в вызывающей функции:

1.Если объект данных является встроенным типом данных, используйте указатель. Если вы обнаружите такой код, как fixit (& x), где x - это int, то ясно, что эта функция намеревается изменить x.

2.Если объект данных является массивом, используйте ваш единственный выбор: указатель.

3.Если объект данных является структурой, используйте ссылку или указатель.

4.Если объект данных является объектом класса, используйте ссылку.

Конечно, это всего лишь рекомендации, и могут быть причины для выбора. Например, cin использует ссылки для основных типов, поэтому вы можете использовать cin >> n вместо cin >> & n.


0

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

int *pInt = new int; // allocates *pInt on the Heap

0

Вы правильно написали пример должен выглядеть

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

Вот почему ссылки предпочтительнее, если это возможно ...


-1

Просто вкладываю свои десять центов. Я только что провела тест. Подлый в этом. Я просто позволил g ++ создавать файлы сборки одной и той же мини-программы с использованием указателей по сравнению с использованием ссылок. При взгляде на вывод они точно такие же. Кроме именования символов. Поэтому, глядя на производительность (в простом примере), проблем нет.

Теперь на тему указателей против ссылок. ИМХО, думаю, ясность стоит превыше всего. Как только я читаю неявное поведение, мои пальцы начинают скручиваться. Я согласен, что это хорошее неявное поведение, что ссылка не может быть NULL.

Разыменование нулевого указателя не является проблемой. это приведет к краху вашего приложения и будет легко отладить. Большая проблема - неинициализированные указатели, содержащие недопустимые значения. Это, скорее всего, приведет к повреждению памяти, вызывающему неопределенное поведение без ясного происхождения.

Здесь я думаю, что ссылки намного безопаснее, чем указатели. И я согласен с предыдущим утверждением, что интерфейс (который должен быть четко задокументирован, см. Проектирование по контракту, Бертран Мейер) определяет результат параметров функции. Теперь, принимая все это во внимание, мои предпочтения идут на использование ссылок везде, где это возможно.


-2

Для указателей они должны указывать на что-то, поэтому указатели стоят места в памяти.

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

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


4
Нет. Функция, которая принимает указатель, не требует выделения переменной указателя: вы можете передать временную переменную &address. Ссылка, безусловно, будет стоить памяти, если она является членом объекта, и, кроме того, все существующие компиляторы фактически реализуют ссылки как адреса, поэтому вы ничего не экономите с точки зрения передачи параметров или разыменования.
underscore_d
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.