2009-03-06 3 views
52

Являются ли эти же:Разница между указателем и ссылочным параметром?

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

и

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

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

ли этот ломтик ничего:

bar& ref = *ptr_to_bar; 

ответ

58

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

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

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

Вы на самом деле можете присвоить NULL к ссылке, если вы приводите его к указателю типа, а затем разыменования точки например int & ref = * (int *) NULL; , и вы также можете переназначить ссылку, если вы поместите ее в союз и измените соответствующее значение в объединении. –

+0

И я должен добавить, что это никогда не должно быть сделано, потому что когда функция запрашивает ссылку, она никогда не ожидает NULL, и хотя большинство ссылок в основном реализованы так же, как указатели, это НЕ МОЖЕТ быть в случае всех сценариев/платформы. –

+0

, если вы назначаете соответствующее значение объединения, то вы действительно не назначаете ссылку? – shoosh

3

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

4

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

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

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

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

+0

Фактически, они, вероятно, генерируют идентичный машинный код, поэтому в этом смысле они одинаковы. – jmucchiello

13

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

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

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

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

3

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

  • Вы не можете присвоить NULL к ссылке (shoosh говорил об этом): это значение, так как нет «не определено» или «недействительные» ссылка значение.

  • Вы можете передать временную переменные как сопзИте ссылки, но это не законно, чтобы передать указатель к временному.

Например, это хорошо:

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, ссылка только указывает на одну вещь в ее вся жизнь.

4

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

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

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

11

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

Для Ex:

 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 
Смежные вопросы