разница между указателем и ссылочным параметром? - PullRequest
62 голосов
/ 07 марта 2009

Это то же самое:

int foo(bar* p) {
  return p->someInt();
}

и

int foo(bar& r) {
  return r.someInt();
}

Игнорировать потенциал нулевого указателя. Являются ли эти две функции функционально идентичными, независимо от того, является ли someInt() виртуальным или если им передано bar или подкласс bar?

Делает ли это что-нибудь:

bar& ref = *ptr_to_bar;

Ответы [ 8 ]

64 голосов
/ 07 марта 2009

C ++ ссылки намеренно не указаны в стандарте, который будет реализован с использованием указателей. Ссылка больше похожа на «синоним» переменной, чем на нее. Эта семантика открывает некоторые возможные оптимизации для компилятора, когда можно понять, что указатель в некоторых ситуациях будет излишним.

Еще несколько отличий:

  • Вы не можете присвоить NULL ссылку. Это принципиальная разница, и Основная причина, по которой вы бы предпочли один из Другой.
  • Когда вы берете адрес указатель, вы получите адрес переменная указателя. Когда вы берете адрес ссылки, вы получите адрес переменной см.
  • Вы не можете переназначить ссылку. После инициализации он указывает на один и тот же объект в течение всей своей жизни.
14 голосов
/ 07 марта 2009

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

И нет, ваша линия не нарезается. Это просто привязка ссылки непосредственно к объекту, на который указывает указатель.

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

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

11 голосов
/ 07 марта 2009

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

Например:

       int j = 10;
       int &i = j;
       int l = 20;
       i = l; // Now value of j = 20

       int *k = &j;
       k = &l;   // Value of j is still 10
5 голосов
/ 07 марта 2009

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

Также важно увидеть семантическую разницу:

  • Используйте ссылку, когда вы фактически передаете объект нормально - но он настолько велик, что имеет смысл передавать ссылку на объект, а не делать копию (если вы не изменяете объект, который является).
  • Используйте указатель, когда вы хотите иметь дело с адресом памяти, а не с объектом.
5 голосов
/ 07 марта 2009

Я давно не использовал C ++, поэтому даже не собираюсь пытаться ответить на ваш вопрос (извините); Тем не менее, Эрик Липперт только что опубликовал отличную статью об указателях / ссылках, на которые, как я полагал, я бы вам указал.

4 голосов
/ 07 марта 2009

Не уверен, что кто-то ответил на ваш второй вопрос, спрятанный внизу о нарезке ... нет, это не приведет к нарезке.

Слайсинг - это когда производный объект назначается (копируется) объекту базового класса - специализация производного класса «обрезается». Обратите внимание, что я сказал, что объект скопирован, речь идет не о копируемых / назначаемых указателях, а о самих объектах.

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

3 голосов
/ 07 марта 2009

Как уже упоминалось, ссылки на реализацию и указатели в основном совпадают. Есть несколько незначительных предостережений:

  • Вы не можете присвоить NULL ссылку (шош упомянул это): это значительный, так как нет "неопределенная" или "недействительная" ссылка значение.

  • Вы можете пройти временный переменная как const ссылка, но не разрешено передавать указатель временно.

Например, это нормально:

class Thingy; // assume a constructor Thingy(int,int)
void foo(const Thingy &a)
{ 
   a.DoSomething();
}

void bar( ) 
{
  foo( Thingy(1,2) );
}

но большинство компиляторов будут жаловаться на

void foo2( Thingy * a);

void bar2()
{
  foo( &Thingy(1,2) );
}
  • Взятие адреса переменной для получения указателя заставляет компилятор сохранить его в памяти. Назначение ссылки на локальную переменную просто создает синоним; в некоторых случаях это может позволить компилятору хранить данные в реестре и избежать load-hit-store . Однако это относится только к локальным переменным - если что-то передается в качестве параметра по ссылке, его нельзя сохранить в стеке.

void foo()
{
   int a = 5;
   // this may be slightly more efficient
   int &b = a;
   printf( "%d", ++b );
   // than this
   int *c = &a;
   printf( "%d", ++(*c) );
}
  • Аналогично, ключевое слово ___ restrict нельзя применять к ссылкам, только к указателям.

  • Вы не можете делать арифметику указателей со ссылками, поэтому, если у вас есть указатель на массив, то следующий элемент в массиве может быть получен через p + 1, ссылка только когда-либо указывает на одну вещь вся его жизнь.

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

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

...