C ++: Почему статически созданная переменная может быть передана функции, ожидающей ссылку? - PullRequest
3 голосов
/ 17 апреля 2011

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

Представьте, что у меня есть функция, которая ожидает один параметр:

void doSomething(SomeClass& ref)
{
    // do something interesting
}

(Примечание: параметр является ссылкой на SomeClass) Затем я вызываю функцию следующим образом:

int main(int argc, char *argv[])
{
    SomeClass a;
    doSomething(a);
}

Почему это допустимый C ++?Функция ожидает ссылку на SomeClass, но я передаю ей статически размещенную переменную типа SomeClass.Ссылка как указатель нет?Если мы заменим ссылку указателем, компилятор пожалуется.Почему ссылка отличается от указателя таким образом, что происходит за кулисами?

Извините, если это глупый вопрос, он просто дал мне сообщение!

Ответы [ 8 ]

2 голосов
/ 17 апреля 2011

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

  1. Ссылки позволяют передавать объекты в функции, позволяя изменять их. Это был популярный пример использования указателей в C.
  2. Реализация указателей и ссылок обычно почти одинакова после компиляции.

Но они - это разные вещи. Вы можете думать о ссылках как о способе дать новое имя объекту. int& x = y; говорит, что я хочу дать новое имя объекту, который я сейчас называю y. Это новое имя x. Оба идентификатора x и y теперь оба ссылаются на один и тот же объект.

Вот почему вы передаете сам объект в качестве ссылки. Вы говорите, что хотите, чтобы функция имела свой собственный идентификатор для ссылки на объект, который вы передаете. Если вы не поместите амперсанд в список параметров, тогда объект будет скопирован в функцию. Это копирование часто не требуется.

1 голос
/ 17 апреля 2011

Возможно, ваша путаница возникает из-за того, что если у вас есть две функции:

void doThingA(int a) {
    a=23;
}
void doThingB(int &a) {
    a=23;
} 

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

int a=10;
doThingA(a);
doThingB(a);

В первом случае doThingA (int) создает совершенно новую переменную со значением 10, присваивает ей 23 и возвращает.Исходная переменная в вызывающей стороне остается неизменной.Во втором случае, doThingB (int &), где переменная передается по ссылке, создается новая переменная create с тем же адресом , что и переданная переменная. Это то, что люди имеют в виду, когда говорят, что передают по ссылке.это как проход по указателю.Поскольку обе переменные имеют один и тот же адрес (занимают место в памяти), когда doThingB (int &) изменяет передаваемое значение, переменная в вызывающей программе также изменяется.

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

void doThingB(const int &a);

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

void doThingB(int *a);
1 голос
/ 17 апреля 2011
SomeClass a();

Это сигнатура функции, а не объект.

должно быть

SomeClass a; // a is an object

Тогда ваш код действителен.

Почему это законный C ++?

(при условии, что вы исправили предыдущую точку) Стандарт C ++ говорит, что если ваш атрибут функции является ссылкой, то вы должны предоставить объект, который имеет имя (l-значение). Так вот, это законно. Если бы это была константная ссылка, вы могли бы даже предоставить временное значение (значение r, у которого нет имени).

Функция ожидает ссылку в SomeClass, но я передаю его статически размещаемая переменная типа SomeClass.

Ожидается ссылка на неконстантный экземпляр SomeClass, что вы и предоставили. Этот экземпляр не статичен, он просто размещен в стеке. Распределение объекта не имеет ничего общего с тем, как им можно манипулировать, только область видимости. То, как объект распределяется (в стеке, как здесь, или в куче с помощью new / delete), только говорит о времени жизни объекта. Даже статический объект может быть передан в вашу функцию, если он не постоянный.

Я думаю, что вы смешиваете некоторые понятия языка здесь ...

Ссылка как указатель нет?

номер

Ссылка - это псевдоним объекта. Не больше, не меньше.

Ладно, на самом деле он реализован в виде указателя со специальными правилами, но это не так во всех случаях: компилятор может реализовать его любым удобным для него способом. В случае атрибута функции, он часто реализуется как указатель. Но вам даже не нужно об этом знать.

Для вас это просто псевдоним объекта.

Если бы мы должны были заменить ссылку с указателем компилятор жалуется. Почему ссылка отличается от указатель таким образом, что происходит за кадром?

Полагаю, твоя первая ошибка сделала тебя неясным?

1 голос
/ 17 апреля 2011

Ваш код неверен - SomeClass a(); является предварительным объявлением функции a, возвращающей экземпляр SomeClass - такое объявление недопустимо в области действия функции.

Предполагается, что вы имели в виду SomeClass a;:

Ссылка очень похожа на указатель в большинстве практических способов - главное отличие состоит в том, что вы не можете юридически иметь ссылку на NULL, тогда как вы можете иметь указатель на NULL.Как вы заметили, синтаксис для указателей и ссылок отличается - вы не можете передать указатель там, где ожидается ссылка.

Если вы думаете о ссылке как о «указателе, который не может быть нулевыми не может быть указано в другом месте "вы в значительной степени покрыты.Вы передаете что-то, что относится к вашему локальному экземпляру a - если doSomething изменяет его параметр, то он действительно напрямую изменяет ваш локальный a.

1 голос
/ 17 апреля 2011

Вы не передаете это «статически распределенная переменная типа SomeClass», вы передаете ему ссылку на SomeClass объект, который вы создали в стеке в main().

Ваша функция

void doSomething(SomeClass& ref)

Вызывает передачу ссылки на a в main. Это то, что & после типа в списке параметров делает.

Если вы опустите &, будет вызван конструктор копирования SomeClass (a), и ваша функция получит новую локальную копию объекта a в main(); в этом случае все, что вы сделали с ref в функции, не было бы видно в main()

0 голосов
/ 13 января 2013

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

Как программисты на C, у нас есть две опции, доступные при передачеПеременные для функций:

  1. Передать значение переменной (создание новой копии)
  2. Передать указатель на переменную.

Когда оно придетна C ++ мы обычно имеем дело с объектами.Копировать такие объекты при каждом вызове функции, который должен работать с этим объектом, не рекомендуется из-за нехватки места (а также скорости).Передача адреса переменной имеет свои преимущества (с помощью подхода с указателем), и хотя мы можем сделать указатель «const», чтобы избежать каких-либо изменений с помощью указателя, синтаксис с указателями является довольно неуклюжим (пропустите оператор разыменования в месте илидва и в конечном итоге тратят часы на отладку!).

C ++, предоставляя «ссылки», объединяет в себе лучшие из двух вариантов:

  1. Ссылку можно понимать так же, какпередача адреса
  2. Синтаксис использования ссылки такой же, как и работа с самой переменной.
  3. Ссылка всегда будет указывать на «что-то».Следовательно, нет исключений 'нулевого указателя'.

Кроме того, если мы создаем ссылку 'const', мы запрещаем любые изменения исходной переменной.

0 голосов
/ 17 апреля 2011

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

Учтите это:

Someclass a;

Someclass& b = a;
Someclass& c = a;

Здесь мы сначала создаем объект a, а затем говорим, что b и c - это другие имена для того же объекта. Ничего нового не создано, только два дополнительных имени.

Когда b или c является параметром для функции, псевдоним для a становится доступным внутри функции, и вы можете использовать его для ссылки на реальный объект.

Это так просто! Вам не нужно прыгать через петли с &, * или ->, как при использовании указателей.

0 голосов
/ 17 апреля 2011

Ссылка не указатель. Вы просто передаете параметры как «по значению» и используете его. Под капотом будет использоваться только указатель, но это только под капотом.

...