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

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

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

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

 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++ )? Есть ли проблемы с производительностью с тем или другим?

EDIT (OUTDATED):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

С C ++ FAQ Lite –

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

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

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

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

Мое правило:

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

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

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

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

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

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

Изменить 2012-03-13

Пользователь Брет Кунс справедливо замечает:

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

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

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

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

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

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

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

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

 add_one(&a); 

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

 add_one(a); 

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

 void add_one(int* const n); 

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

 foo(int* const a, int b); 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Скопировано из wiki –

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

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

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

 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 

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Следует иметь в виду:

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

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

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

  4. Use pointers when pointer arithmetic operation are required.

  5. You can have pointers to a void type int a=5; void *p = &a; but cannot have a reference to a void type.

Pointer Vs Reference

 void fun(int *a) { cout< 

Verdict when to use what

Pointer : For array, linklist, tree implementations and pointer arithmetic.

Reference : In function parameters and return types.

I prefer to use pointers. At least it is clear what you are doing. I have the feeling that references are mostly used because of STL and its syntax implications on code. Because of that also so many C++ standard library novelties like std::move ….. to get exactly what you want, and not what you intuitively would have thought of.

Use references as a last resort. Allocate an instance on the stack or the heap, use them.

Use references for parameter scope to get the least impact. If you use reference because pointers are too hard for you then move to another language.

  • В C, какой правильный синтаксис для объявления указателей?
  • Сколько уровней указателей у нас есть?
  • возвращая локальную переменную из функции в C
  • Как вернуть std :: string.c_str ()
  • Как использовать массивы на C ++?
  • Когда имя массива или имя функции «преобразуется» в указатель? (в С)
  • Почему массивы в C распадаются на указатели?
  • Почему для нулевого указателя используется нулевой адрес?
  • Как создается результирующая структура локального времени в C?
  • Разница указателей / адресов
  • Эффективность: массивы против указателей
  • Давайте будем гением компьютера.