Функции C++: амперсанд и звездочка
Допустим, у вас есть функция, которая изменяет переменную.
Если вы напишите это так: void myfunc(int *a)
или как это void myfunc(int &a)
?
Первое заставляет вас вызывать функцию с myfunc(&b)
поэтому звонящий знает, что b
будет изменен, но последний короче и может быть вызван просто с myfunc(b)
, Так что лучше использовать? Что-то еще мне не хватает?
9 ответов
Указатели (т. Е. '*') Должны использоваться там, где значение "NULL" имеет смысл. Например, вы можете использовать NULL для представления того, что конкретный объект должен быть создан или что конкретное действие не нужно предпринимать. Или если это когда-либо нужно вызывать из не-C++ кода. (например, для использования в общих библиотеках)
например. Функция libc time_t time (time_t *result);
Если result
не NULL, текущее время будет сохранено. Но если result
NULL, то никаких действий не предпринимается.
Если функции, которую вы пишете, не нужно использовать NULL в качестве значимого значения, то использование ссылок (т. Е. '&'), Вероятно, будет менее запутанным - при условии, что это соглашение, которое использует ваш проект.
По возможности я использую ссылки над указателями. Причина этого в том, что намного сложнее испортить ссылку, чем указатель. Люди всегда могут передать NULL в значение указателя, но такого эквивалента для ссылки нет.
Единственным недостатком является то, что ссылочные параметры в C++ не имеют документации по сайту вызовов. Некоторые люди считают, что это затрудняет понимание кода (и я согласен до некоторой степени). Я обычно определяю следующее в своем коде и использую его для документации сайта поддельных вызовов
#define byref
...
someFunc(byref x);
Это, конечно, не приводит в исполнение документацию сайта вызовов. Это просто обеспечивает очень неудачный способ документирования. Я провел некоторые эксперименты с шаблоном, который обеспечивает документирование сайта вызовов. Это больше для удовольствия, чем для реального производственного кода.
http://blogs.msdn.com/jaredpar/archive/2008/04/03/reference-values-in-c.aspx
Я думаю, что я не согласен с @bb и @JaredPar, и я склоняюсь к противоположной стороне забора. После многих лет попыток поддержать код C++ других людей, я часто сталкиваюсь с проблемами, скрывающимися в неочевидных побочных эффектах ссылочных аргументов. С C# это очевидно, так как вы должны префиксировать типы аргументов с помощью 'ref'/'out', но ссылки потенциально запутаны в C++. Итак, мне нравятся указатели, потому что действительно ясно, что что-то возвращается. Если вам не нравятся очки, C++ не для вас.
Я спускаюсь на указательную сторону забора по причинам, указанным здесь и в других местах. Тем не менее, я скажу, что что бы вы ни решили, вы должны быть последовательными и документировать это в своем руководстве по стилю.
Руководство по стилю Google C++ запрещает неконстантные ссылочные аргументы.
Что следует отметить, если вы используете функторы stl, проще, если параметр соответствует типу значения контейнера.
void foo(Bar *);
void frobnicate(vector<Bar *> vecBars)
{
for_each(vecBars.begin(),
vecBars.end(),
ptr_fun(&foo));
}
Приведенный выше код намного сложнее, если foo берет Bar&
Мне нравится передавать по ссылке, если значение NULL не имеет значения, но я вижу аргументы для обоих. Если вы осторожны с кодированием, вы, вероятно, можете устранить случайное возражение, связанное с передачей по ссылке, убедившись, что вы всегда передаете переменные по константной ссылке, например:
myfunc( const_cast< const int& >( a ) );
// Alternatively, this approach may require additional handling
// in the function, but it's cleaner at call point
myfunc( boost::cref( a ) );
Это много дополнительного кода для небольшой выгоды, хотя. Как указал Кенни, C# обращался к этому с противоположной стороны (требуя особой передачи по ссылке), но это не вариант для C++ (если, например, вы не написали свои функции, чтобы использовать в качестве параметра оболочку ссылки, например boost::ref(param)), например:
void myfunc( const boost::reference_wrapper< int >& a ) { ... }
Исправление проблемы с указателем более проблематично, хотя... во время компиляции не существует способа убедиться, что указатель действителен, поэтому вы в конечном итоге сталкиваетесь либо с проблемами времени выполнения для проблем с указателем, либо с проверками времени выполнения, либо с обоими. Это природа указателей.
Во всяком случае, это только мое мнение, для чего это стоит.
Первая заставляет вас вызывать функцию с помощью myfunc(&b), чтобы вызывающий знал, что b будет изменен
иногда функция может принять указатель const, и вызывающая сторона будет неправильно думать, что b будет изменен.
Моя рекомендация - предпочитайте использовать ссылки везде, где это возможно (конечно, там, где это необходимо). В случае с аргументом функции - мы получаем преимущества:
- ссылки не могут быть NULL - это помогает нам избежать ошибок и ненужных утверждений или проверок.
- ссылки имеют только одну точку инициализации, а в функции boody вы всегда знаете, на что указывает входной параметр.
Я сопровождаю большой проект. И в любом случае я смотрю на определение функции перед вызовом ее. Конечно, когда я смотрю на определение функции, я вижу определение аргументов по значению, по ссылке, по константной ссылке или по указателю.
Но это похоже на вопрос о священной войне, у разных народов разные взгляды на это. Например, соглашение о кодировке Google рекомендует использовать указатели в аргументах, которые можно изменить, и допускаются только постоянные ссылки: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments
Все параметры, передаваемые по ссылке, должны быть помечены как const.
Одно из отличий, как уже упоминалось ранее, заключается в том, что вы не можете передать нулевую ссылку, но вы можете передать нулевой указатель.
Еще одна вещь, также уже упоминалось, когда вы звоните f(a,b)
может быть путаница, если звонящий не знает, что f
потенциально может изменить значение для b
Тем не менее, еще одна проблема, которая является довольно тонкой, но я все же столкнулся с ней, это семантика ссылок.
Указатели передаются по значению, а ссылки - нет.
Это означает, что если вы передадите параметр по указателю, вы можете изменить указатель и настроить его на что-то другое.
Учти это:
void f1_ptr( type * a )
{
a = new type(); //no change to passed parameters, you're changing the pointer which was passed by value
}
void f2_ptr( type * a )
{
*a = some_other_value; //now you're changing the value of the parameter that was passed
//or, if type is a class or struct:
a->some_method_that_modifies_object(); //again, changing the parameter that was passed
}
Но при передаче по ссылке вы не можете изменить ссылку, чтобы ссылаться на другое значение. Как только ссылка установлена, она не может быть изменена.
void f3_ref( type& a )
{
a = type(); //the referred variable has also changed
}
//....
type obj = type( params );
f3_ref( obj ); //obj now changed
f1_ptr( &obj ); //obj doesn't change
f2_ptr( &obj ); //obj now changed
В идеальном мире все сводится к тому, является ли параметр необязательным выходным сигналом. Вы хотите, чтобы вызывающий абонент передал NULL, если он не заботится об этом? Затем используйте указатель. В противном случае ссылка является лучшим выбором.
Обратите внимание, что в обоих случаях язык затрудняет документирование выходного параметра. Гораздо лучше, если вся кодовая база является правильной, тогда пользователи могут предположить, что любой неконстантный параметр ссылки или указателя является выходным.