В C++ языке программирования, ссылка - это простой тип данных ссылка, который менее эффективен, но более безопасен, чем тип указателя , унаследованный от C. Название C ++ ссылка может вызвать путаницу, так как в информатике ссылка - это общий концептуальный тип данных, а указатели и ссылки C ++ являются конкретными реализациями ссылочного типа данных. Определение ссылки в C ++ таково, что она может не существовать. Его можно реализовать как новое имя для существующего объекта (аналогично ключевому слову rename в Ada).
Объявление формы:
, где
- это тип, а
- это идентификатор, тип которого ссылка на
.
Примеры:
int a = 5; int r_a = a; extern int r_b;
Здесь r_a
и r_b
относятся к типу "ссылка на int
"
int Foo ();
Foo
- это функция, которая возвращает ссылка на int
"
void Bar (int r_p);
Bar
- это функция со ссылочным параметром, который является "ссылкой на int
"
class MyClass {int m_b; / *... * /};
MyClass
- это класс
с членом, который ссылается на int
int FuncX () {return 42;}; int (f_func) () = FuncX;
FuncX
is функция, которая возвращает (не ссылочный тип) int
, а f_func
является псевдонимом для FuncX
const int ref = 65;
const int ref
- постоянная ссылка, указывающая на часть хранилища, имеющую значение 65.
Типы, которые относятся к типу «ссылка на
», иногда называются ссылкой типы . Идентификаторы ссылочного типа называются ссылочными переменными . Однако называть их переменными, как мы увидим, на самом деле неправильно.
Ссылки C ++ отличаются от указателей несколькими существенными способами:
int k; // компилятор будет жаловаться: ошибка: `k 'объявлен как ссылка, но не инициализирован
Существует простое преобразование между указателями и ссылками: оператор адресации () даст указатель, ссылающийся на тот же объект при применении к ссылке и ссылка, которая инициализируется разыменованием (
*
) значения указателя, будет ссылаться на тот же объект, что и этот указатель, где это возможно без вызова неопределенного поведения. Эта эквивалентность является отражением типичной реализации, которая эффективно компилирует ссылки в указатели, которые неявно разыменовываются при каждом использовании. Хотя это обычно так, стандарт C ++ не заставляет компиляторы реализовывать ссылки с помощью указателей.
Следствием этого является то, что во многих реализациях работа с переменной с автоматическим или статическим временем существования через ссылку, хотя синтаксически похожа на доступ к ней напрямую, может включать скрытые операции разыменования, которые являются дорогостоящими.
Кроме того, поскольку операции со ссылками настолько ограничены, их гораздо легче понять, чем указатели, и они более устойчивы к ошибкам. Хотя указатели можно сделать недействительными с помощью различных механизмов, от переноса нулевого значения до арифметики за пределы границ и недопустимых приведений до их создания из произвольных целых чисел, ранее действительная ссылка становится недействительной только в двух случаях:
Первое легко обнаружить автоматически, если ссылка имеет статическую область видимости, но все еще является проблемой, если ссылка является членом динамически выделяемого объекта; второй обнаружить труднее. Это единственные проблемы со ссылками, которые соответствующим образом решаются разумной политикой распределения.
void Square (int x, int out_result) {out_result = x * x; }
Затем следующий вызов поместит 9 в y:
int y; Квадрат (3, у);
Однако следующий вызов приведет к ошибке компилятора, поскольку ссылочные параметры, не уточненные с помощью const
, могут быть привязаны только к адресуемым значениям:
Square (3, 6);
int Preinc (int x) {return ++ x; // "return x ++;" было бы неправильно} Preinc (y) = 5; // то же, что ++ y, y = 5
const
, являются полезным способом передачи больших объектов между функциями, который позволяет избежать этих накладных расходов: void FSlow (BigObject x) {/ *... * /} void FFast (const BigObject x) {/ *... * /} BigObject y; FSlow (y); // Медленно, копирует y в параметр x. FFast (y); // Быстро, дает прямой доступ только для чтения к y.
Если FFast
действительно требует своей собственной копии x, которую он может изменять, он должен создать копию явно. Хотя тот же метод может быть применен с использованием указателей, это потребует изменения каждого сайта вызова функции, чтобы добавить к аргументу громоздкие операторы адресации (), и будет одинаково сложно отменить, если в дальнейшем объект стал меньше.
Продолжая взаимосвязь между ссылками и указателями (в контексте C ++), первые демонстрируют полиморфные возможности, как и следовало ожидать:
#includeclass A {public : A () = по умолчанию; virtual void Print () {std :: cout << "This is class A\n"; } }; class B : public A { public: B() = default; virtual void Print() { std::cout << "This is class B\n"; } }; int main() { A a; Aref_to_a = a; B b; Aref_to_b = b; ref_to_a.Print(); ref_to_b.Print(); }
Приведенный выше источник является действительным C ++ и генерирует следующий вывод:. Это класс A
Это класс B
Ссылки определяются стандартом ISO C ++ следующим образом (за исключением раздела примеров):
В объявлении TD, где D имеет форму
D1
и тип идентификатора в объявлении T D1 - это «список производных-деклараторов-типов T
», тогда тип идентификатора D - «ссылка на список-производных-деклараторов-типов 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. Ссылка должна быть инициализирована для ссылки на действительный объект или функцию. [Примечание: в частности, пустая ссылка не может существовать в четко определенной программе, потому что единственный способ создать такую ссылку - это привязать ее к «объекту», полученному путем разыменования нулевого указателя, что вызывает неопределенное поведение. Как описано в 9.6, ссылку нельзя напрямую привязать к битовому полю. ]