Функции C ++: амперсанд и звездочка - PullRequest
34 голосов
/ 22 марта 2009

Допустим, у вас есть функция, которая изменяет переменную.

Если вы напишите это так: void myfunc(int *a) или вот так void myfunc(int &a)?

Первый вынуждает вас вызывать функцию с myfunc(&b), чтобы вызывающий знал, что b будет изменен, но последний короче и его можно вызвать просто с помощью myfunc(b). Так что лучше использовать? Что-то еще мне не хватает?

Ответы [ 9 ]

39 голосов
/ 22 марта 2009

Указатели (т. Е. '*') Должны использоваться там, где значение "NULL" имеет смысл. Например, вы можете использовать NULL для представления того, что конкретный объект должен быть создан или что конкретное действие не нужно предпринимать. Или если это когда-либо нужно вызывать из не-C ++ кода. (например, для использования в общих библиотеках)

например. Функция libc time_t time (time_t *result);

Если result не равно NULL, текущее время будет сохранено. Но если result равно NULL, то никаких действий не предпринимается.

Если функции, которую вы пишете, не нужно использовать NULL в качестве значащего значения, то использование ссылок (то есть '&'), вероятно, будет менее запутанным - при условии, что это соглашение, которое использует ваш проект. 1010 *

12 голосов
/ 22 марта 2009

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

Единственный реальный недостаток - в ссылочных параметрах C ++ отсутствует документация по сайту вызова. Некоторые люди считают, что это затрудняет понимание кода (и я согласен до некоторой степени). Я обычно определяю следующее в своем коде и использую его для документации сайта поддельных вызовов

#define byref
...
someFunc(byref x);

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

http://blogs.msdn.com/jaredpar/archive/2008/04/03/reference-values-in-c.aspx

7 голосов
/ 22 марта 2009

Думаю, я бы не согласился с @bb и @JaredPar и наклонился к противоположной стороне забора. После многих лет попыток поддержать код C ++ других людей, я часто сталкиваюсь с проблемами, скрывающимися в неочевидных побочных эффектах ссылочных аргументов. С C # это очевидно, так как вы должны префиксировать типы аргументов с помощью 'ref' / 'out', но ссылки потенциально запутаны в C ++. Итак, мне нравятся указатели, потому что действительно ясно, что что-то возвращается. Если вам не нравятся очки, C ++ не для вас.

6 голосов
/ 22 марта 2009

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

Руководство по стилю Google C ++ Запреты Неконстантные ссылочные аргументы.

1 голос
/ 25 марта 2009

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

void foo(Bar *);

void frobnicate(vector<Bar *> vecBars)
{
   for_each(vecBars.begin(), 
            vecBars.end(), 
            ptr_fun(&foo));
}

Приведенный выше код намного сложнее, если foo берет Bar &

1 голос
/ 22 марта 2009

Мне нравится передавать по ссылке, если значение 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 ) { ... }

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

Во всяком случае, это только мое мнение, для чего оно стоит.

1 голос
/ 22 марта 2009

Бывший заставляет вас называть работать с myfunc (& b), чтобы вызывающий знает, что b будет изменен

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

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

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

Но это похоже на вопрос о священной войне, у разных народов разные взгляды на это. Например. Соглашение Google Codding рекомендовало использовать указатели в аргументах, которые могут быть изменены, и допускаются только константные ссылки: http://google -styleguide.googlecode.com / SVN / багажник / cppguide.xml # Reference_Arguments

Все параметры передаются по ссылке должен иметь маркировку const.

0 голосов
/ 22 марта 2009

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

Еще одна вещь, также уже упоминавшаяся, когда вы вызываете 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
0 голосов
/ 22 марта 2009

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

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

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