Разыменование указателя при передаче по ссылке

что происходит, когда вы разыскиваете указатель при передаче по ссылке на функцию?

Вот простой пример

int& returnSame( int &example ) { return example; }

int main()
{
  int inum = 3;
  int *pinum = & inum;

  std::cout << "inum: " <<  returnSame(*pinum) << std::endl;

  return 0;          

}

Создан ли временный объект?

Ответ 1

Разыменование указателя не создает копию; он создает lvalue, который ссылается на цель указателя. Это может быть связано с аргументом ссылки lvalue, поэтому функция получает ссылку на объект, на который указывает указатель, и возвращает ссылку на то же самое. Это поведение четко определено, и не задействован временный объект.

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

Ответ 2

Ответ на ваш вопрос как написано

Нет, это поведение определено. Никакие конструкторы не вызывают, когда типы указателей разыменовываются или используются ссылочные типы, если явно не указано программистом, как со следующим фрагментом, в котором оператор new вызывает конструктор по умолчанию для типа int.

int* variable = new int;

Что касается того, что на самом деле происходит, как написано, returnSame(*pinum) является той же переменной, что и inum. Если вам хочется проверить это самостоятельно, вы можете использовать следующий фрагмент:

returnSame(*pinum) = 10;
std::cout << "inum: " << inum << std::endl;

Дальнейший анализ

Я начну с исправления вашего предоставленного кода, который не выглядит так, как вы пытались скомпилировать его перед публикацией. После редактирования одна оставшаяся ошибка находится в первой строке:

int& returnSame( int &example ) { return example; } // semi instead of colon

Указатели и ссылки

Указатели и ссылки обрабатываются компилятором одинаково, они различаются по своему использованию, а не только по их реализации. Типы указателей и типы ссылок сохраняют в качестве своего значения местоположение чего-то еще. Разрушение указателя (с использованием операторов * или ->) инструктирует компилятор для создания кода для отслеживания указателя и выполнения операции в местоположении, к которому он относится, а не о самом значении. Никакие новые данные не выделяются при разыменовании указателя (конструкторы не вызываются).

Использование ссылок работает почти таким же образом, за исключением того, что компилятор автоматически предполагает, что вы хотите значение в местоположении, а не в самом месте. На самом деле невозможно ссылаться на местоположение, указанное ссылкой, таким же образом, как указатели позволяют: после присвоения ссылки нельзя повторно использовать (изменить) (то есть, не полагаясь на поведение undefined), однако вы все равно можете получить его значение, используя на нем оператор &. Возможно даже иметь ссылку NULL, хотя обработка этих файлов особенно сложна, и я не рекомендую их использовать.

Анализ фрагментов

int *pinum = & inum;

Создает указатель, указывающий на существующую переменную, inum. Значение указателя - это адрес памяти, в котором хранится inum. Создание и использование указателей НЕ будет вызывать конструктор для заостренного объекта неявно, КОГДА-ЛИБО. Эта задача оставлена ​​программисту.

*pinum

Разыменование указателя эффективно создает регулярную переменную. Эта переменная может концептуально занимать одно и то же пространство, которое использует другая именованная переменная, а может и нет. в этом случае *pinum и inum являются одной и той же переменной. Когда я говорю "производит", важно отметить, что никакие конструкторы не называются. Вот почему вы ДОЛЖНЫ инициализировать указатели перед их использованием: разыменование указателя НИКОГДА не выделяет хранилище.

returnSame(*pinum)

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

int* returnSamePointer( int *example ) { return example; }

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

std::cout << "inum: " <<  *(returnSamePointer(pinum)) << std::endl;

Ссылки NULL

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

int& nullRef = *((int *) NULL);      // creates a reference to nothing
bool isRefNull = (&nullRef == NULL); // true

Резюме

  • Типы указателей и ссылок - это два разных способа выполнить одно и то же.
  • Большинство исправлений, которые применяются к одному, применяются к другому
  • Ни указатели, ни ссылки не вызовут конструкторы или деструкторы для ссылочных значений неявно ни при каких обстоятельствах.
  • Объявление ссылки на разыменованный указатель совершенно законно, если указатель инициализирован правильно

Ответ 3

Компилятор ничего не "вызывает". Он просто генерирует код. Выделение указателя на самом базовом уровне соответствует какой-либо инструкции по загрузке, но в настоящем коде компилятор может легко оптимизировать это и просто распечатать значение напрямую или, возможно, непосредственно перейти к загрузке inum.

Относительно вашего "временного объекта": разыменование указателя всегда дает lvalue.

Возможно, есть еще более интересный вопрос, спрятанный в вашем вопросе: Как компилятор реализует передаваемые аргументы функции как ссылки?