Ссылки в C++ для начинающих. Повторение и продолжение знакомства

Эта тема является повторением и продолжением темы Ссылки в C++ для начинающих. Поверхностное знакомство.
Ссылки в С++ — это отдельный (невстроенный) тип данных. В большинстве случаев ссылочные переменные работают как автоматически разыменовываемые указательные переменные. Из-за очень сильной похожести между двумя разными типами (ссылка и указатель) новичкам трудно, очень трудно уловить тонкое различие. Если бы ссылке дали название ярлык, то, возможно, проблем с пониманием было намного меньше: ссылка напоминает ярлыки программ. Большинство из вас, мои читатели, прекрасно знает, что такое ярлык к документу, к программе, к игре. Основной файл один, а ссылающихся на него ярлыков может быть бесконечно много. Такие вот ссылающиеся ярлыки, только ссылающиеся не на программы, а на какие-то именованные переменные, называются ссылками.

  • Ссылка — это второе имя объекта. Псевдоним объекту.
  • Навешивание ссылок на переменные == навешивание переменным личных ярлыков.
Часто говорят, что ссылка — это саморазыменовывающийся указатель. Это близко к правде, но неправда. Говорят так только для того, чтобы спросивший смог быстрее понять суть-существование ссылки. Опытные программисты, умеющие читать документацию, прекрасно знают, что в разных компиляторах ссылки могут иметь отличающиеся реализации. На выходе-то одно, но устройство разное.
На самом деле увидеть пример, где ссылочная переменная не ведёт себя как саморазыменованный указатель очень просто:

Но в целом это допущение, что ссылка — саморазыменовывающийся указатель, помогает многим новичкам. Чем же ссылка принципиально отличается от указателя? — этот вопрос тревожит армию новичков постоянно.
  • Синтаксис. Ссылка по свойствам напоминает указатель, но при написании ссылочные переменные не требуют принудительного разыменования.
  • Ссылка есть только в C++. Указатель есть и в C++, и в C.
  • В отличие от указателя ссылку нельзя перенаправить ни на какой новый адрес, ссылку нельзя переназначить. В этом плане ссылка эквивалентна константному указателю.
  • В отличие от указателя, который может быть не инициализирован, ссылка требует обязательной инициализации.
  • Ссылка может указывать на указатель. Указатель на ссылку указывать не может.
  • Уровень косвенности у указателя может быть очень большим (указатели на указатели). Ссылка допускает только один уровень косвенности: нельзя иметь ссылку на ссылку.
  • const-ссылка может продлевать время жизни временного объекта, на который она указывает. У указателя такая особенность отсутствует.
  • Нельзя иметь массив ссылок. Массив указателей — можно.
  • В отличие от указательной переменной, которая всегда занимает место в памяти, ссылочная переменная может не занимать место в памяти вообще (зависит от реализации компилятора).
  • Ссылка не встронный тип данных, поэтому говорить, что ссылка указывает на адрес или говорить, что ссылка указывает на название привязанной к себе переменной — некорректно.
Мне никогда не нравилось определение ссылки: второе имя переменной. Оно, считаю, откровенно выбрано неудачно и приводит к абсолютному непониманию большинством новичков. Я могу обозначить ссылку как представителя переменной, и такое определение будет намного больше соответствовать действительности, чем даваемое нам и поныне. Указатель больше как агент: способен менять клиентов и даже место жительства (смещение указателя, если неконстантный). Под клиентами понимаем переменные и значения, хранимые в памяти, на которые заточен указатель в какой-то момент времени.
Ссылки использовать удобнее указателей, потому что не приходится задумываться, где разыменовывать переменную, где не разыменовывать. Использование ссылок, как и использование указателей, зачастую помогает ускорить выполнение функции, избегая накладных расходов на копирование. Принимать приходящие вовнутрь функций аргументы вовнутрь константных ссылок, если приходящие аргументы представляют собой большие структуры данных, считается хорошим тоном.
Если прикинуть какие-нибудь аналогии, то ссылки подобны агентам-посредникам в то время, когда указатели больше похожи на курьеров. К переменной можно обратиться напрямую, а можно через любой из, возможно, множества псевдонимов, т. е. задействовать ссылку, отчего получается, что между программистом и переменной есть третья сторона, напоминающая агента-посредника. В случае работы с указателем указателю задаётся адрес и даётся план работ: или измени значение внутри этого адреса, или сравни значения, или даётся указание смены адреса. Так получается, что мне указатель напоминает курьера с правами давать задачи рабочему классу (изменение значений не сам же указатель делает, а, условно, рабочий класс: операционная система).
На самом деле без ссылок вполне можно обойтись, но коды будут сложнее: одни константные указатели, указатели на константы и константные указатели на константы чего стоят! А тут константную ссылку обозначил и живёшь, не путаешься, не горюешь. В этом плане использование ссылок безопаснее использования указателей. Вдобавок, свойство продлевать жизнь временной переменной находит себе применение при написании шаблонов.
Одно из очень замечательных свойств ссылок — сохранять данные об объекте. Например, если ссылка на массив, то при передаче ссылки на массив в функцию, самой функции необязательно передавать данные о размере массива, эта информация будет сохранена ссылкой:

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

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

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

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

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

Статья полностью переписана 28.03.2018

Один комментарий на «“Ссылки в C++ для начинающих. Повторение и продолжение знакомства”»

  1. ander:

    Ссылок на самом деле в программе не существует, также как и имен переменных.  Используются они только при компиляции и линковке.  А вот указатель — это вполне реальная ячейка памяти, содержащая адрес.

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

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

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

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/lubov-zalogova/razrabotka-paskal-kompilyatora/?lfrom=15589587
Яндекс.Метрика