Урок 98. Передача аргументов по ссылке

   ⁄ 

 Обновлено 3 Дек 2017

  ⁄   

Хотя передача по значению хороший вариант во многих случаях, но она имеет несколько ограничений. Во-первых, при передаче по значению большой структуры или класса в функцию, создается копия аргумента и уже эта копия передается в параметр функции. Во многих случаях это бесполезная трата ресурсов, которая ухудшает производительность. Во-вторых, при передаче аргументов по значению единственный способ вернуть значение обратно в вызывающий объект (в caller) — использовать возвращаемое значение функции. И иногда случаются ситуации, когда нужно, чтобы функция изменила значение переданного аргумента. Передача по ссылке решает все эти проблемы.

Передача по ссылке

Для передачи переменной по ссылке – нужно просто объявить параметры функции как ссылки, а не как обычные переменные:

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

В следующем примере это хорошо проиллюстрировано:

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

value = 6
value = 7

Как вы можете видеть, функция изменила значение аргумента из 6 на 7!

Вот еще один пример:

Результат:

a = 7
a = 8

Обратите внимание, значение аргумента a было изменено функцией.

Возвращение нескольких значений через параметры вывода

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

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

Давайте рассмотрим, как это работает более подробно. Во-первых, в функции main() мы создаем локальные переменные sin и cos. Они передаются в функцию getSinCos() по ссылке (а не по значению). Это означает, что функция getSinCos() имеет прямой доступ к исходным значениям переменных sin и cos, а не к их копиям. getSinCos() соответственно присваивает новые значения переменным sin и cos (через ссылки sinOut и cosOut), переписывая их старые значения. Затем main() выводит эти обновленные значения.

Если бы sin и cos были переданы по значению, а не по ссылке, функция getSinCos() изменила бы копии sin и cos, а не исходные значения и эти изменения удалились бы в конце функции – переменные вышли бы из локальной области видимости. Но поскольку sin и cos передавались по ссылке, то любые изменения, внесенные в sin или cos (через ссылки), сохраняются и за пределами функции getSinCos(). Таким образом, мы можем использовать этот механизм для возврата сразу нескольких значений обратно в caller.

Хотя этот способ хорош, но он также имеет несколько нюансов. Во-первых, синтаксис немного неестественен, так как параметры ввода и вывода указываются вместе в вызове функции. Во-вторых, в caller-е не очевидно, что sin и cos – это параметры вывода, и они будут изменены функцией. Это, вероятно, самая опасная часть этого способа (так как может привести к ошибкам). Некоторые программисты и компании считают, что это достаточно большая проблема, и не советуют передавать аргументы по ссылке, а вместо неё советуют использовать передачу по адресу, не смешивая при этом параметры ввода и вывода.

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

Ограничения передачи по ссылке

Неконстантные ссылки могут ссылаться только на неконстантные l-values (например, на неконстантные переменные), поэтому параметр-ссылка не может принять аргумент, который является константным l-value или r-value (например, литералом или результатом выражения).

Передача по константной ссылке

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

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

Вы уже знаете, что константная ссылка — это ссылка на переменную, значение которой изменить через эту же ссылку не получиться никак. Следовательно, если мы используем константную ссылку в качестве параметра, то получаем 100% гарантию того, что функция не изменит аргумент!

В компиляции следующего фрагмента кода мы получим ошибку компилятора:

Использование const полезно по нескольким причинам:

 мы получаем помощь компилятора в обеспечении того, что значения, которые не должны быть изменены, не изменятся (компилятор выдаст ошибку, если мы попытаемся сделать подобное — как в примере выше);

 программист, видя const, понимает, что функция не изменит значение аргумента. Это может помочь при отладке программы;

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

 константные ссылки могут принимать любые типы аргументов, включая l-values, константные l-values ​​и r-values.

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

Плюсы и минусы передачи по ссылке

Плюсы передачи по ссылке:

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

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

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

Минусы передачи по ссылке:

  Трудно определить, является ли параметр, переданный по неконстантной ссылкой, параметром ввода, вывода или того и другого одновременно. Разумное использование const и суффикса Out для внешних переменных решает эту проблему.

  Из вызова функции невозможно определить, будет ли аргумент изменен функцией или нет. Аргумент, переданный по значению или по ссылке, выглядит одинаково. Мы можем определить, передается ли аргумент по значению или по ссылке, только просмотрев объявление функции. Это может привести к ситуациям, когда программист не сразу поймет, что функция изменяет значение аргумента.

Когда использовать передачу по ссылке:

  при передаче структур или классов (используйте const, если нужно только для чтения);

  когда нужно, чтобы функция изменяла значение аргумента.

Когда не использовать передачу по ссылке:

  при передаче фундаментальных типов данных (используйте передачу по значению);

  при передаче обычных массивов (используйте передачу по адресу).

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

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (7 оценок, среднее: 4,43 из 5)
Загрузка...
Поделиться в:
Подписаться на обновления:

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

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