Ссылка (C++)

Материал из Seo Wiki - Поисковая Оптимизация и Программирование

Перейти к: навигация, поиск

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

Содержание

Синтаксис и терминология

Объявление вида:

<Type> & <Name>

где <Type>тип и <Name>идентификатор, указывают идентификатор, чьим типом является ссылка на <Type>.

Примеры:

  1. int A = 5;
  2. int& rA = A;
  3. extern int& rB;
  4. int& foo ();
  5. void bar (int& rP);
  6. class MyClass { int& m_b; /* ... */ };
  7. int funcX() { return 42 ; }; int (&xFunc)() = funcX;

Здесь, rA и rB являются типами «ссылок на int», foo() — функция, возвращающая ссылку на int, bar() — функция с ссылкой в качестве параметра, которая ссылается на int, MyClass — класс (class) с членом, ссылающимся на int, funcX() — функция, возвращающая int, xFunc() — псевдоним для funcX.

Типы, относящиеся к «ссылка на <Type>», иногда называются ссылочными типами. Идентификаторы ссылочного типа называются ссылочными переменными. Вызывать их переменную фактически будет неправильно (показано дальше).

Связь с указателями

C++ ссылки отличаются от указателей несколькими особенностями:

  • Невозможно ссылаться напрямую на объект ссылочного типа после его определения; каждое упоминание его имени ссылается напрямую на объект, на который он ссылается.
  • В качестве результата первого указания не могут быть выполнены ни какие-либо арифметические вычисления, ни приведение типов, ни любые другие операции, кроме копирования их связанных значений в другие ссылки.
  • После создания ссылки ее нельзя переделать в ссылку на другой объект; в таких случаях говорят, не может быть переопределена. Это часто делают с указателями.
  • Ссылки не могут быть null (т.е.указывать в никуда), тогда как указатели - могут; каждая ссылка ссылается на некий объект, вне зависимости от его корректности.
  • Ссылки не могут быть неинициализированными. Так как невозможно переинициализировать ссылку, она должна быть инициализирована сразу после создания. В частности, локальные и глобальные переменные должны быть проинициализированы там же, где они определены, а ссылки, которые являются данными-членами сущностей класса, должны быть инициализированы в списке инициализатора конструктора класса.

Пример:

int& k; // компилятор выдаст сообщение: ошибка: 'k' declared as reference but not initialized ('k' объявлена как ссылка, но не инициализирована)

Существует простое преобразование между указателями и ссылками: операция взятия адреса (&) получает указатель, ссылающийся на тот же самый объект при переходе по ссылке, а ссылка, которая инициализирована при разыменовании (*) указателя будет указывать на тот же объект, что и указатель, где это возможно без неопределенного поведения. Эта тождественность - отражение типичной реализации, которая весьма эффективно превращает ссылки в указатели, которые неявно разыменовываются при каждом использовании.

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

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

  • Если она ссылается на объект с автоматическим размещением в памяти, которое выходит за границы видимости,
  • Если она ссылается на объект, находящийся в блоке динамической памяти, который был освобожден.

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

Применение ссылок

  • Помимо удобной замены указателям, еще одним полезным применением ссылок являются списки параметров функции, при помощи которых они могут передавать параметры, используемые для вывода без явного взятия адреса вызывающим. Например:
void square(int x, int& result) {
      result = x * x;
  }

Тогда следующий вызов поместит 9 в y:

square(3, y);

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

square(3, 6);
  • Возврат ссылки также позволяет непредусмотренный синтаксис, в котором вызовы функции могут быть присвоены:
int& preinc(int& x) { ++x; return x; }
preinc(y) = 5; // то же, что и ++y, y = 5
  • Во многих реализациях, обычные механизмы передачи параметров часто подразумевают весьма полезную операцию копирования больших параметров. Ссылки, помеченные const, полезны в качестве способа передачи больших объектов между функциями без накладных расходов:
void f_slow(BigObject x) { /* ... */ }  
void f_fast(const BigObject& x) { /* ... */ }
BigObject y;
f_slow(y); // медленно, копирует y в параметр x
f_fast(y); // быстро, дает прямой доступ к y (только для чтения)

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

Цитаты

Ссылки определены стандартом ISO C++ следующим образом (исключая раздел примеров):

В объявлении T D, где D имеет вид
& D1

а типом идентификатора в объявлении T D1 является производный тип ("derived-declarator-type-list) T,” тогда типом идентификатора D будет производная (“derived-declarator-type-list) ссылка на T.” Cv-ссылки являются плохо согласованными, исключая ситуацию, когда cv-квалификаторы (от англ. const ("константный") и volatile ("временный")) представлены через использование typedef (7.1.3) или шаблон аргумента типа (14.3), в случае чего игнорируются cv-квалификаторы. [Пример: в коде

typedef int& A;
const A aref = 3; // плохо согласовано;
// неконстантная ссылка инициализируется rvalue

тип aref является “ссылкой на int”, а не “const ссылается на int”. ] [Примечание: ссылка может восприниматсья как имя объекта. ] Объявление, указывающее тип “ссылается на cv void”, некорректно.

Это не определяет, требуется ли ссылке выделение памяти (3.7).

Не должно быть ссылок на ссылки, массивов ссылок, а также указателей на ссылки. Объявление ссылки должно содержать инициализатор (8.5.3), за исключением случая, когда объявление содержит явный указатель extern (7.1.1) - объявление члена класса (9.2) внутри объявления класса, объявление параметра или возврат типа (8.3.5); смотри 3.1. Ссылка должна при инициализации ссылаться на корректный объект или функцию. [Примечание: точнее говоря, ссылка на null не может существовать в корректно написанной программе, так как единственным способом создать подобную ссылку является связывание ее с "объектом", полученном при помощи разыменования нуль-указателя, что вызывает неопределенное поведение. Как описано в 9.6, ссылка не может ограничиваться напрямую битовым полем. ]

| cite = ISO/IEC 14882:1998(E), стандарт ISO C++, раздел 8.3.2 [dcl.ref]


Дополнительные источники

Личные инструменты

Served in 0.158 secs.