C ++: разница между амперсандом "&" и звездочкой "*" в объявлении функции / метода? - PullRequest
64 голосов
/ 27 февраля 2009

Есть ли какая-то тонкая разница между ними:

void a1(float &b) {
    b=1;
};
a1(b);

и

void a1(float *b) {
    (*b)=1;
};
a1(&b);

Они оба делают одно и то же (или так кажется из main ()), но первый, очевидно, короче, однако большая часть кода, который я вижу, использует вторую запись. Есть ли разница? Может в случае, если это какой-то объект вместо плавающего?

Ответы [ 7 ]

51 голосов
/ 27 февраля 2009

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

Смотрите мой ответ здесь для полного списка всех различий .

23 голосов
/ 27 февраля 2009

Да. Обозначение * говорит, что то, что передается в стеке, является указателем, то есть адресом чего-либо. & говорит, что это ссылка. Эффект похож, но не идентичен:

Давайте рассмотрим два случая:

   void examP(int* ip);
   void examR(int& i);

   int i;

Если я звоню examP, я пишу

   examP(&i);

, который берет адрес элемента и передает его в стек. Если я позвоню examR,

   examR(i);

Мне это не нужно; теперь компилятор «каким-то образом» передает ссылку - что практически означает, что он получает и передает адрес i. На стороне кода, тогда

   void examP(int* ip){
        *ip += 1;
   }

Я должен разыменовать указатель. ip += 1 делает что-то совсем другое.

   void examR(int& i){
        i += 1;
   }

всегда обновляет значение i.

Чтобы больше думать, прочитайте «вызов по ссылке» против «вызов по значению». Понятие & дает вызов C ++ по ссылке.

5 голосов
/ 27 февраля 2009

В первом примере со ссылками вы знаете, что b не может быть NULL. В примере с указателем b может быть указателем NULL.

Однако обратите внимание, что возможно передать объект NULL через ссылку, но это неудобно, и вызываемая процедура может предположить, что сделала ошибку:

a1(*(float *)NULL);
3 голосов
/ 27 февраля 2009

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

3 голосов
/ 27 февраля 2009

Во втором примере вызывающая сторона должна префиксировать имя переменной с '&', чтобы передать адрес переменной.

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

1 голос
/ 27 февраля 2009

Функционально в вашем примере обе версии работают одинаково.

Первое имеет преимущество в том, что оно прозрачно на стороне вызова. Представьте, как бы выглядел оператор:

cin >> &x;

И как это выглядит некрасиво для вызова подкачки

swap(&a, &b);

Вы хотите поменять местами a и b. И это выглядит намного лучше, чем когда вы впервые берете адрес. Кстати, Бьярн Страуструп пишет, что основной причиной ссылок была прозрачность, которая была добавлена ​​на стороне вызова - особенно для операторов. Также посмотрите, как это не очевидно больше, если следующий

&a + 10

Добавил бы 10 к содержимому a, вызвав оператор + этого, или добавил ли он 10 к временному указателю на a. Добавьте это к невозможности перегрузить операторы только для встроенных операндов (таких как указатель и целое число). Ссылки делают это кристально чистым.

Указатели полезны, если вы хотите иметь возможность поставить «ноль»:

a1(0);

Тогда в a1 метод может сравнить указатель с 0 и посмотреть, указывает ли указатель на какой-либо объект.

0 голосов
/ 28 февраля 2009

Одна большая разница, которую стоит отметить, это то, что происходит снаружи, у вас либо есть:

a1(something);

или

a1(&something);

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

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