разница между указателем и ссылочным параметром?

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

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

и

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

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

делает ли этот срез что-нибудь:

bar& ref = *ptr_to_bar;

8 ответов


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

еще несколько различий:

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

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

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

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

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


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

Например:

       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

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

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

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

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


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

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

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


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

  • вы не можете присвоить 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 keyword не может применяться к ссылкам, только указатели.

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


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