Разница между указателем и ссылочным параметром?
Являются ли они такими же:
int foo(bar* p) {
return p->someInt();
}
и
int foo(bar& r) {
return r.someInt();
}
Игнорировать потенциал нулевого указателя. Являются ли эти две функции функционально идентичными независимо от того, является ли someInt()
виртуальным или если они переданы a bar
или подкласс bar
?
Этот фрагмент ничего:
bar& ref = *ptr_to_bar;
Ответы
Ответ 1
Ссылки на С++ намеренно не указаны в стандарте, который должен быть реализован с помощью указателей. Ссылка скорее похожа на синоним переменной, чем указатель на нее. Эта семантика открывает некоторые возможные оптимизации для компилятора, когда можно понять, что указатель будет чрезмерным в некоторых ситуациях.
Еще несколько отличий:
- Вы не можете назначить NULL ссылке.
Это ключевое различие, и
Основная причина, по которой вы предпочли бы
Другие.
- Когда вы берете адрес
указатель, вы получаете адрес
переменная указателя. Когда вы берете
адрес ссылки, вы получаете
адрес переменной, являющейся
упоминается.
- Вы не можете переназначить ссылку. После инициализации он указывает на тот же объект на протяжении всей своей жизни.
Ответ 2
Игнорирование каждого синтаксического сахара и возможностей, которые могут быть сделаны с одним, а не с другим, и разница между указателями и ссылками, объясняемыми в других ответах (на другие вопросы)... Да, эти два функционально одинаковы! Оба вызова функции, и оба одинаково хорошо справляются с виртуальными функциями.
И нет, ваша строка не нарезается. Это просто привязывает ссылку непосредственно к объекту, на который указывает указатель.
Некоторые вопросы о том, почему вы хотели бы использовать один над другим:
Вместо того, чтобы пытаться самостоятельно разобраться с различиями, я делегирую вас тем, кто хочет знать.
Ответ 3
Ссылка является константным указателем, то есть вы не можете изменить ссылку для ссылки на другой объект. Если вы измените, изменится значение объекта ссылки.
Для 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
Ответ 4
Да, они функционально идентичны. Поскольку ссылка потребует от вас установки объекта перед его использованием, вам не придется иметь дело с указателями нулей или указателями на недопустимую память.
Также важно увидеть семантическую разницу:
- Используйте ссылку, если вы действительно передадите объект нормальному - но он настолько велик, что имеет смысл передавать ссылку на объект, а не делать копию (если вы не изменяете объект, который есть).
- Используйте указатель, если вы хотите иметь дело с адресом памяти, а не с объектом.
Ответ 5
Я не использовал C++ долгое время, поэтому я даже не собираюсь пытаться действительно ответить на ваш вопрос (извините); Тем не менее, Эрик Липперт только что опубликовал отличную статью о указателях/ссылках, на которые, как я полагал, я бы вам указал.
Ответ 6
Не уверен, что кто-то ответил на ваш второй вопрос, скрытый внизу о разрезе... нет, что не вызовет нарезки.
Нарезка - это когда производный объект присваивается (копируется) объекту базового класса - специализация производного класса "разрезана". Обратите внимание, что я сказал, что объект скопирован, мы не говорим о копировании/назначении указателей, но сами объекты.
В вашем примере это не происходит. Вы просто удаляете указатель на объект Bar (тем самым вызывая объект Bar), который используется как rvalue в инициализации ссылок. Не уверен, что я получил свою терминологию правильно...
Ответ 7
Как и все остальные, в ссылках на реализацию и указателях в основном то же самое. Есть некоторые незначительные оговорки:
-
Вы не можете назначить NULL ссылке
(shoosh упомянул об этом): это
так как нет
"undefined" или "недействительная" ссылка
значение.
-
Вы можете передать временный
переменная как ссылка 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) );
}
-
Аналогично, __ limit keyword не может применяться к ссылкам, только указатели.
-
Вы не можете делать арифметику указателей со ссылками, поэтому, если у вас есть указатель на массив, то следующий элемент массива может быть получен через p + 1, ссылка только указывает на одну вещь в всю свою жизнь.
Ответ 8
Функции, очевидно, не "одинаковы", но в отношении виртуального поведения они будут вести себя одинаково. Что касается разрезания, это происходит только тогда, когда вы обрабатываете значения, а не ссылки или указатели.