Урок №88. Ссылки

  Юрий  | 

  |

  Обновл. 24 Янв 2022  | 

 136464

 ǀ   8 

До этого момента мы успели рассмотреть 2 основных типа переменных:

   обычные переменные, которые хранят значения напрямую;

   указатели, которые хранят адрес другого значения (или null), для доступа к которым выполняется операция разыменования указателя.

Ссылки — это третий базовый тип переменных в языке C++.

Ссылки

Ссылка — это тип переменной в языке C++, который работает как псевдоним другого объекта или значения. Язык C++ поддерживает 3 типа ссылок:

   Ссылки на неконстантные значения (обычно их называют просто «ссылки» или «неконстантные ссылки»), которые мы обсудим на этом уроке.

   Ссылки на константные значения (обычно их называют «константные ссылки»), которые мы обсудим на следующем уроке.

   В C++11 добавлены ссылки r-value, о которых мы поговорим чуть позже.

Ссылка (на неконстантное значение) объявляется с использованием амперсанда (&) между типом данных и именем ссылки:

В этом контексте амперсанд не означает «оператор адреса», он означает «ссылка на».

Ссылки в качестве псевдонимов


Ссылки обычно ведут себя идентично значениям, на которые они ссылаются. В этом смысле ссылка работает как псевдоним объекта, на который она ссылается, например:

Результат выполнения программы:

9
10

В примере, приведенном выше, объекты ref и value обрабатываются как одно целое. Использование оператора адреса с ссылкой приведет к возврату адреса значения, на которое ссылается ссылка:

Краткий обзор l-value и r-value

На уроке №10 мы уже рассматривали, что такое l-value и r-value. l-value — это объект, который имеет определенный адрес памяти (например, переменная x) и сохраняется за пределами одного выражения. r-value — это временное значение без определенного адреса памяти и с областью видимости выражения (т.е. сохраняется в пределах одного выражения). В качестве r-values могут быть как результаты выражения (например, 2 + 3), так и литералы.

Инициализация ссылок


Ссылки должны быть инициализированы при создании:

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

Ссылки на неконстантные значения могут быть инициализированы только неконстантными l-values. Они не могут быть инициализированы константными l-values или r-values:

Обратите внимание, во втором случае вы не можете инициализировать неконстантную ссылку константным объектом. В противном случае, вы бы могли изменить значение константного объекта через ссылку, что уже является нарушением понятия «константа».

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

Обратите внимание, во втором стейтменте (ref = value2;) выполняется не то, что вы могли бы ожидать! Вместо переприсваивания ref (ссылаться на переменную value2), значение из value2 присваивается переменной value1 (на которое и ссылается ref).

Ссылки в качестве параметров в функциях

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

На уроке №82 мы говорили о том, что передача аргумента-указателя в функцию позволяет функции при разыменовании этого указателя напрямую изменять значение аргумента.

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



Результат выполнения программы:

7
8

Когда аргумент x передается в функцию, то параметр функции ref становится ссылкой на аргумент x. Это позволяет функции изменять значение x непосредственно через ref! Обратите внимание, переменная x не обязательно должна быть ссылкой.

Совет: Передавайте аргументы в функцию через неконстантные ссылки-параметры, если они должны быть изменены функцией в дальнейшем.

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

Ссылки как более легкий способ доступа к данным


Второе (гораздо менее используемое) применение ссылок заключается в более легком способе доступа к вложенным данным. Рассмотрим следующую структуру:

Предположим, что нам нужно работать с полем value1 структуры Something переменной other структуры Other (звучит сложно, но такое также встречается на практике). Обычно, доступ к этому полю осуществлялся бы через other.something.value1. А что, если нам нужно неоднократно получать доступ к этому члену? В этом случае код становится громоздким и беспорядочным. Ссылки же предоставляют более легкий способ доступа к данным:

Таким образом, следующие два стейтмента идентичны:

Ссылки позволяют сделать ваш код более чистым и понятным.

Ссылки vs. Указатели

Ссылка — это тот же указатель, который неявно разыменовывается при доступе к значению, на которое он указывает («под капотом» ссылки реализованы с помощью указателей). Таким образом, в следующем коде:

*ptr и ref обрабатываются одинаково. Т.е. это одно и то же:

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

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

Заключение

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

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (517 оценок, среднее: 4,93 из 5)
Загрузка...

Комментариев: 8

  1. Дмитрий:

    А сама ссылка имеет свою ячейку памяти?

  2. SASF:

    Можно узнать более подробно о разнице ссылок и указателей, в особенности при оптимизации кода

    1. Сергей:

      С этим вопросом лучше обратиться к ассемблеру, а именно к сегментным регистрам, индексным регистрам, базовым регистрам. А особенно их использовании при адресации памяти.
      https://ravesli.com/assembler-sistemnye-vyzovy-i-rezhimy-adresatsii/
      Ассемблер. Системные вызовы и режимы адресации

  3. Константин:

    А кстати, только у меня такая фича что если сделать константную ссылку на неконстантную переменную, то при изменении оригинальной переменной будет менятся и ссылка на неё? Хотя по идее это логично, но если бы это было не так, а ссылка хранила значение, записанное в неё во время создания, можно было бы это использовать довольно много где

    1. v:

      Даже звучит сомнительно: "вот бы ссылка хранила значение". Ссылка хранит только ссылку на значение в памяти, можете как угодно менять значение ссылка указывает на все тот же адрес памяти. А для вашего случая нужна переменная, которая хранит значение.

  4. Алексей:

    Самое забавное, что в игре Doom'16 похоже разработчики просто забыли освободить ячейки памяти.
    20 мин работаешь в их SnapMap и у тебя на 1-2 гига памяти больше требуется, так же с видео памятью — увеличено потребление.

    Вот уж не знаю если закончу С++ здесь, что будет далее.
    bash тоже не любил, сейчас просто незаменим при обработке данных.

    1. Sergey:

      А bash как учили-полюбили? книгу или сайт посоветуйте

    2. Ivan:

      bash незаменим для обработки данных? Вы серьёзно? Он уже морально и технически устарел. Даже для нужд администрирования Python вместо него с радостью применяют.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *