Я понимаю синтаксис и общую семантику указателей по сравнению со ссылками, но как мне решить, когда более или менее целесообразно использовать ссылки или указатели в 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)
, вызовет версию указателя и прервется во время выполнения), имеет смысл с точки зрения сопровождения использовать ссылки, где ДОЛЖЕН присутствовать объект, хотя это позор. потерять синтаксическую ясность.
add_one(a);
неясно, чтоa
будет изменено? Это говорит прямо в коде: добавьте один .addOneTo(...)
. Если это не то, что вы хотите сделать, просто посмотрите на объявление.Ответы:
Используйте ссылки, где вы можете, указатели, где вы должны.
Избегайте указателей, пока не сможете.
Причина в том, что указатели затрудняют чтение / чтение, делают их менее безопасными и намного более опасными манипуляциями, чем любые другие конструкции.
Таким образом, эмпирическое правило заключается в использовании указателей, только если нет другого выбора.
Например, возвращение указателя на объект является допустимой опцией, когда функция может возвращать nullptr в некоторых случаях, и предполагается, что это будет. Тем не менее, лучшим вариантом будет использовать что-то похожее на
boost::optional
.Другой пример - использование указателей на необработанную память для определенных манипуляций с памятью. Это должно быть скрыто и локализовано в очень узких частях кода, чтобы помочь ограничить опасные части всей кодовой базы.
В вашем примере нет смысла использовать указатель в качестве аргумента, потому что:
nullptr
в качестве аргумента, вы попадете в неопределенную область поведения;Если поведение функции должно работать с заданным объектом или без него, то использование указателя в качестве атрибута предполагает, что вы можете передать его
nullptr
в качестве аргумента, и это нормально для функции. Это своего рода контракт между пользователем и реализацией.источник
add_one(a)
не вернуть результат, а не установить его по ссылке?add_one(a)
сбивает с толку, то это потому, что он неправильно назван.add_one(&a)
будет иметь такую же путаницу, только теперь вы можете увеличивать указатель, а не объект.add_one_inplace(a)
позволит избежать путаницы.NULL
иnullptr
, и он имеет их по причине. И это не продуманный и даже не реальный совет давать «никогда не используйте указатели» и / или «никогда не используйте NULL, всегда используйтеboost::optional
». Это просто безумие. Не поймите меня неправильно, необработанные указатели нужны в C ++ реже, чем в C, но, тем не менее, они полезны, они не настолько «опасны», как любят утверждать некоторые люди C ++ (это тоже преувеличение), и снова: когда проще просто использовать указатель иreturn nullptr;
указать отсутствующее значение ... Зачем импортировать весь Boost?if
устарел, и нужно использоватьwhile() { break; }
вместо этого, верно? Кроме того, не волнуйтесь, я видел и работал с большими базами кода, и да, если вы неосторожны , то владение является проблемой. Но если вы придерживаетесь соглашений, используйте их последовательно, комментируйте и документируйте свой код. Но в конце концов, я должен просто использовать C, потому что я слишком туп для C ++, верно?Представления точно такие же, поскольку ссылки реализованы внутри как указатели. Таким образом, вам не нужно беспокоиться об этом.
Не существует общепринятого соглашения о том, когда использовать ссылки и указатели. В некоторых случаях вы должны возвращать или принимать ссылки (например, конструктор копирования), но вы можете делать это по своему усмотрению. Я встречал довольно распространенное соглашение - использовать ссылки, когда параметр должен ссылаться на существующий объект, и указатели, когда значение NULL в порядке.
Некоторые соглашения по кодированию (например, Google ) предписывают, что всегда следует использовать указатели или константные ссылки, потому что ссылки имеют немного неясный синтаксис: они имеют ссылочное поведение, но синтаксис значения.
источник
add_one
будет сломано :add_one(0); // passing a perfectly valid pointer value
, Kaboom. Вы должны проверить, если это ноль. Некоторые люди возразят: «Хорошо, я просто задокументирую, что моя функция не работает с нулем». Это нормально, но тогда вы проигнорируете цель вопроса: если вы посмотрите документацию, чтобы увидеть, в порядке ли null, вы также увидите объявление функции .int& i = *((int*)0);
это неверная реплика. Проблема в предыдущем коде связана с использованием указателя, а не ссылки . Ссылки никогда не бывают нулевыми, точка.Из C ++ FAQ Lite -
источник
C with class
(что не является C ++).Мое правило:
&
)const
входной параметр)const T&
).int ¤t = someArray[i]
)Независимо от того, какой из них вы используете, не забудьте документировать свои функции и значение их параметров, если они не очевидны.
источник
Отказ от ответственности: кроме того факта, что ссылки не могут быть NULL или «отскок» (то есть они не могут изменить объект, псевдоним которого они имеют), это действительно сводится к вопросу вкуса, поэтому я не собираюсь говорить "это лучше".
Тем не менее, я не согласен с вашим последним утверждением в посте, так как я не думаю, что код теряет ясность со ссылками. В вашем примере
может быть яснее, чем
так как вы знаете, что, скорее всего, значение изменится. С другой стороны, хотя подпись функции
также не совсем понятно: будет ли n целым числом или массивом? Иногда у вас есть доступ только к (плохо документированным) заголовкам и таким подписям
не легко интерпретировать с первого взгляда.
Имхо, ссылки так же хороши, как указатели, когда не требуется (пере) перераспределение или повторное связывание (в смысле, объясненном ранее). Более того, если разработчик использует только указатели для массивов, сигнатуры функций несколько менее неоднозначны. Не говоря уже о том, что синтаксис операторов гораздо удобнее читать по ссылкам.
источник
Как и другие уже ответили: ссылки всегда использовать, если переменная является
NULL
/nullptr
не является на самом деле действительным состоянием.Точка зрения Джона Кармака на эту тему похожа:
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
Редактировать 2012-03-13
Пользователь Bret Kuhns справедливо отмечает:
Достаточно верно, но вопрос все еще остается, даже при замене сырых указателей умными указателями.
Например, оба
std::unique_ptr
иstd::shared_ptr
могут быть созданы как «пустые» указатели через их конструктор по умолчанию:... имея в виду, что их использование без проверки того, что они не являются пустыми, может привести к краху, и это именно то, о чем говорит Дж. Кармак.
И затем, у нас есть забавная проблема "как мы передаем умный указатель в качестве параметра функции?"
Jon «сек ответ на вопрос C ++ - передача ссылки на повышение :: shared_ptr , и следующие комментарии показывают , что даже тогда, передавая смарт - указатель на копию или по ссылке не столь четкого среза , как хотелось бы (я одобряю себя в" по ссылке "по умолчанию, но я могу ошибаться).
источник
shared_ptr
, иunique_ptr
. Семантика владения и условные обозначения параметров входа / выхода обеспечиваются сочетанием этих трех частей и постоянством. Почти нет необходимости в необработанных указателях в C ++, за исключением случаев, когда речь идет об устаревшем коде и очень оптимизированных алгоритмах. Те области, где они используются, должны быть как можно более инкапсулированы и преобразовывать любые необработанные указатели в семантически подходящий «современный» эквивалент.Это не вопрос вкуса. Вот некоторые окончательные правила.
Если вы хотите сослаться на статически объявленную переменную в области, в которой она была объявлена, используйте ссылку C ++, и она будет совершенно безопасной. То же самое относится и к статически объявленному интеллектуальному указателю. Передача параметров по ссылке является примером такого использования.
Если вы хотите сослаться на что-либо из области, которая шире, чем область, в которой оно объявлено, вам следует использовать интеллектуальный указатель с подсчетом ссылок, чтобы он был совершенно безопасным.
Вы можете ссылаться на элемент коллекции со ссылкой для удобства синтаксиса, но это не безопасно; элемент может быть удален в любое время.
Чтобы безопасно хранить ссылку на элемент коллекции, вы должны использовать умный указатель со счетчиком ссылок.
источник
Любая разница в производительности будет настолько мала, что не будет оправдано использование менее понятного подхода.
Во-первых, один случай, который не был упомянут, когда ссылки, как правило, выше, это
const
ссылки. Для непростых типов передачаconst reference
избегает создания временного и не вызывает путаницы, о которой вы беспокоитесь (потому что значение не изменено). Здесь, принуждение человека к передаче указателя вызывает ту самую путаницу, о которой вы беспокоитесь, поскольку просмотр адреса, взятого и переданного функции, может заставить вас думать, что значение изменилось.В любом случае, я в принципе согласен с вами. Мне не нравятся функции, берущие ссылки для изменения их значения, когда не очень очевидно, что именно это делает функция. Я тоже предпочитаю использовать указатели в этом случае.
Когда вам нужно вернуть значение в сложном типе, я предпочитаю ссылки. Например:
Здесь имя функции дает понять, что вы получаете информацию обратно в массив. Так что нет путаницы.
Основным преимуществом ссылок является то, что они всегда содержат допустимое значение, чище указателей и поддерживают полиморфизм, не требуя дополнительного синтаксиса. Если ни одно из этих преимуществ не применимо, нет причин предпочитать ссылку указателю.
источник
Скопировано из вики -
Я согласен на 100% с этим, и поэтому я считаю, что вы должны использовать ссылку только тогда, когда у вас есть очень веские основания для этого.
источник
Что нужно иметь в виду:
Указатели могут быть
NULL
, ссылки не могут бытьNULL
.Ссылки проще в использовании,
const
их можно использовать для ссылки, когда мы не хотим изменять значение и просто нуждаемся в ссылке в функции.Указатель используется с указателями
*
while, используемыми с&
.Используйте указатели, когда требуется арифметическая операция с указателем.
Вы можете иметь указатели на тип void,
int a=5; void *p = &a;
но не можете иметь ссылку на тип void.Указатель против ссылки
Вердикт, когда использовать что
Указатель : для массива, списка ссылок, реализации дерева и арифметики указателя.
Справка : в параметрах функции и типах возвращаемых данных.
источник
Существует проблема с правилом « использовать ссылки везде, где это возможно », и оно возникает, если вы хотите сохранить ссылку для дальнейшего использования. Чтобы проиллюстрировать это на примере, представьте, что у вас есть следующие классы.
На первый взгляд может показаться хорошей идеей, чтобы параметр в
RefPhone(const SimCard & card)
конструкторе передавался по ссылке, поскольку он предотвращает передачу неверных / нулевых указателей в конструктор. Это как-то поощряет распределение переменных в стеке и использование преимуществ RAII.Но затем приходят временники, чтобы разрушить ваш счастливый мир.
Так что, если вы слепо придерживаетесь ссылок, вы компенсируете возможность передачи недействительных указателей на возможность хранения ссылок на уничтоженные объекты, что в основном имеет тот же эффект.
редактировать: обратите внимание, что я придерживался правила «Используйте ссылки везде, где можете, указатели везде, где вы должны. Избегайте указателей, пока вы не можете». от наиболее одобренного и принятого ответа (другие ответы также предполагают так). Хотя это должно быть очевидно, пример не показывает, что ссылки как таковые являются плохими. Однако ими можно злоупотреблять, как указателями, и они могут создавать свои собственные угрозы в коде.
Существуют следующие различия между указателями и ссылками.
Принимая это во внимание, мои текущие правила заключаются в следующем.
источник
const SimCard & m_card;
это просто плохо написанный код.const SimCard & m_card
это правильно или нет , зависит от контекста. Сообщение в этом посте не о том, что ссылки небезопасны (хотя они могут быть, если кто-то старается). Суть в том, что вы не должны слепо придерживаться заклинания «использовать ссылки, когда это возможно». Пример - это результат агрессивного использования доктрины «используйте ссылки, когда это возможно». Это должно быть ясно.const SimCard & m_card
. Если вы хотите работать с временными специалистами, добавьтеexplicit RefPhone(const SimCard&& card)
конструктор.Ниже приведены некоторые рекомендации.
Функция использует переданные данные без их изменения:
Если объект данных небольшой, например, встроенный тип данных или небольшая структура, передайте его по значению.
Если объект данных является массивом, используйте указатель, потому что это ваш единственный выбор. Сделать указатель указателем на const.
Если объект данных представляет собой структуру хорошего размера, используйте константный указатель или константную ссылку для повышения эффективности программы. Вы экономите время и пространство, необходимые для копирования структуры или дизайна класса. Сделайте указатель или ссылку const.
Если объект данных является объектом класса, используйте константную ссылку. Семантика дизайна класса часто требует использования ссылки, которая является основной причиной, по которой C ++ добавил эту функцию. Таким образом, стандартный способ передачи аргументов объекта класса - это ссылка.
Функция изменяет данные в вызывающей функции:
1.Если объект данных является встроенным типом данных, используйте указатель. Если вы обнаружите такой код, как fixit (& x), где x - это int, то ясно, что эта функция намеревается изменить x.
2.Если объект данных является массивом, используйте ваш единственный выбор: указатель.
3.Если объект данных является структурой, используйте ссылку или указатель.
4.Если объект данных является объектом класса, используйте ссылку.
Конечно, это всего лишь рекомендации, и могут быть причины для выбора. Например, cin использует ссылки для основных типов, поэтому вы можете использовать cin >> n вместо cin >> & n.
источник
Ссылки более понятны и просты в использовании, и они лучше скрывают информацию. Однако ссылки не могут быть переназначены. Если вам нужно указать сначала на один объект, а затем на другой, вы должны использовать указатель. Ссылки не могут быть нулевыми, поэтому, если есть вероятность, что рассматриваемый объект может быть нулевым, вы не должны использовать ссылку. Вы должны использовать указатель. Если вы хотите обрабатывать объекты самостоятельно, т. Е. Если вы хотите выделить пространство памяти для объекта в куче, а не в стеке, вы должны использовать указатель
источник
Вы правильно написали пример должен выглядеть
Вот почему ссылки предпочтительнее, если это возможно ...
источник
Просто вкладываю свои десять центов. Я только что провела тест. Подлый в этом. Я просто позволил g ++ создавать файлы сборки одной и той же мини-программы с использованием указателей по сравнению с использованием ссылок. При взгляде на вывод они точно такие же. Кроме именования символов. Поэтому, глядя на производительность (в простом примере), проблем нет.
Теперь на тему указателей против ссылок. ИМХО, думаю, ясность стоит превыше всего. Как только я читаю неявное поведение, мои пальцы начинают скручиваться. Я согласен, что это хорошее неявное поведение, что ссылка не может быть NULL.
Разыменование нулевого указателя не является проблемой. это приведет к краху вашего приложения и будет легко отладить. Большая проблема - неинициализированные указатели, содержащие недопустимые значения. Это, скорее всего, приведет к повреждению памяти, вызывающему неопределенное поведение без ясного происхождения.
Здесь я думаю, что ссылки намного безопаснее, чем указатели. И я согласен с предыдущим утверждением, что интерфейс (который должен быть четко задокументирован, см. Проектирование по контракту, Бертран Мейер) определяет результат параметров функции. Теперь, принимая все это во внимание, мои предпочтения идут на использование ссылок везде, где это возможно.
источник
Для указателей они должны указывать на что-то, поэтому указатели стоят места в памяти.
Например, функция, которая принимает целочисленный указатель, не будет принимать целочисленную переменную. Таким образом, вам нужно будет создать указатель для того, чтобы сначала передать функцию.
Что касается справки, это не будет стоить памяти. У вас есть целочисленная переменная, и вы можете передать ее как ссылочную переменную. Вот и все. Вам не нужно создавать ссылочную переменную специально для нее.
источник
&address
. Ссылка, безусловно, будет стоить памяти, если она является членом объекта, и, кроме того, все существующие компиляторы фактически реализуют ссылки как адреса, поэтому вы ничего не экономите с точки зрения передачи параметров или разыменования.