Как создать ссылку на объект c
Перейти к содержимому

Как создать ссылку на объект c

  • автор:

Ссылки (C++)

В ссылке, как и в указателе, хранится адрес объекта, расположенного в другой области памяти. В отличие от указателя, после инициализации ссылку нельзя перенаправить на другой объект или присвоить ей нулевое значение. Существует два типа ссылок: ссылки lvalue, которые ссылаются на именованную переменную и ссылки rvalue, которые ссылаются на временный объект. Оператор > обозначает ссылку lvalue, а оператор &> обозначает либо ссылку rvalue, либо универсальную ссылку (rvalue или lvalue) в зависимости от контекста.

Ссылки могут объявляться с помощью следующего синтаксиса.

[описатель класса хранилища] [cv-qualifiers] type-specifiers [ms-modifier] декларатор [=выражение];

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

Ссылки объявляются с использованием следующей последовательности.

  1. Спецификаторы объявления:
    • Необязательный спецификатор класса хранения.
    • Необязательные const и/или volatile квалификаторы.
    • Спецификатор типа: имя типа.
  2. Декларатор:
    • Необязательный модификатор, используемый в системах Microsoft. Дополнительные сведения см. в разделе «Модификаторы для конкретного майкрософт».
    • Оператор > или оператор &>.
    • Необязательные const и(или) volatile квалифы.
    • Идентификатор.
  3. Необязательный инициализатор.

Более сложные формы декларатора для указателей на массивы и функции также применяются к ссылкам на массивы и функции. Дополнительные сведения см . в указателях.

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

int &i; int &i, &j; 

Ссылки, указатели и объекты могут быть объявлены вместе.

Ссылка содержит адрес объекта, однако с синтаксической точки зрения ведет себя как объект.

В следующей программе обратите внимание, что имя объекта, s и ссылка на объект, SRef , могут использоваться идентично в следующих программах.

Пример

// references.cpp #include struct S < short i; >; int main() < S s; // Declare the object. S& SRef = s; // Declare and initialize the reference. s.i = 3; printf_s("%d\n", s.i); printf_s("%d\n", SRef.i); SRef.i = 4; printf_s("%d\n", s.i); printf_s("%d\n", SRef.i); >
3 3 4 4 

Ссылки в C++

Ссылка в С++ — это альтернативное имя объекта.

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

  1. При объявлении ссылка обязательно на уже существующий объект данного типа. Ссылка не может ссылаться «ни на что».
  2. Ссылка от её объявления до её исчезновения указывает на один и тот же адрес.
  3. При обращении к ссылке разыменование происходит автоматически.
  4. Адрес ссылки — это адрес исходного объекта, на который она указывает.

Объявление ссылок очень похоже на объявление указателей, только вместо звёздочки * пишется амперсанд &.
При объявлении ссылка обязана быть инициализирована.

int &x; // недопустимо!
int &x = veryLongVariableName; // допустимо. Теперь x — это альтернативное имя переменной veryLongVariableName

int A[10];
int &x = A[5]; // Ссылка может указывать на элемент массива
x++; // то же, что A[5]++;
x = 1; // то же, что A[5] = 1;

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

Параметры можно передавать по ссылкам. При этом связывание ссылки с определённой переменной произойдёт в момент вызова функции (на этапе выполнения программы).

void foo(int &x)
x = 3;
>
int main()
int t = 1;
foo (t);
cout >

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

void foo(const int &x);

Это гарантирует программисту-пользователю функции неизменность передаваемого значения.

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

Чего нельзя делать со ссылкой

Возврат из функции ссылки на автоматически созданный объект (локальную переменную) приводит к появлению «битых ссылок», значение которых непредсказуемо.

Также синтаксис С++ не позволяет создавать указатели на ссылки и массивы ссылок.

Указатели и ссылки в языке C++

Указатели представляют собой объекты, значением которых служат адреса других объектов:

  • переменных
  • констант
  • функций
  • других указателей
Объявление указателей

<тип> *<имя_переменной>[,*<имя_переменной>].

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

Инициализация указателей

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

Указателю нельзя присвоить адрес переменной другого типа. То есть нельзя указателю типа int* присвоить адрес переменной типа double.

Также указателю можно присвоить значение другого указателя.

Указатель также может быть проинициализирован пустым значением. Это можно сделать несколькими способами:

  • использовать значение 0 или макроопределение NULL
  • использовать значение nullptr
  • использовать значение std::nullptr_t (C++ 11)

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

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

Напрямую записать адрес в указатель можно только с помощью операций преобразования типов, либо операции reinterpret_cast.

int a = 0; int *p = &a; double v = 0.1; double *pv = &v; char *pc = nullptr;
Разыменование указателей

Для получения значения переменной, на которую ссылается указатель, используется операция разыменования указателя. Эта операция записывается как символ * (звездочка), написанный перед указателем.

int a = 123; int *p = &a; int b = *p; // b присваивается значение 123
Арифметические действия с указателями

С указателем можно производить следующие арифметические действия:

  • сложение и вычитание с целым числом
  • операции инкремента/декремента

При использовании арифметических операций, указатель изменяется на величину кратную размеру типа указателя. Например, если указатель имеет тип 32-разрядного int, то увеличение указателя на 1 приведет к увеличению значения адреса в указателе на 4.

Указатель на указатель

В языке C++ можно объявить указатель, который будет указывать на другой указатель.

Синтаксис объявления такой же, как и у объявления указателя, за исключением того, что ставится два символа * (звездочка).

<тип> **<имя_переменной>[,**<имя_переменной>].

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

int value = 1234; int *p = &value; int **pp = &p; int val = **pp; // 1234

Использовать указатели на указатели может потребоваться, например для создания массива из массивов, и в частности массива из строк.

Язык C++ также позволяет работать с указателями на указатели на указатели, или сделать еще большую вложенность. Их можно объявлять просто увеличивая количество символов * (звездочек). Однако на практике такие указатели используются крайне редко.

Неконстантный указатель на неконстантное значение

int val1 = 10; int val2 = 20; int* ptr = &val1; std::cout 

В этом случае можно изменять как сам указатель, так и значение, на которое он указывает.

Неконстантный указатель на константное значение

const int val1 = 10; const int val2 = 20; const int* ptr = &val1; std::cout 

В этом случае указатель можно изменять. Но само значение, на которое он указывает изменять нельзя.

То же самое поведение можно получить, даже если переменные указаны как неконстантные. Для этого достаточно сам указатель объявить таким образом, чтобы он якобы указывал на константное значение:

int val1 = 10; int val2 = 20; const int* ptr = &val1; std::cout 

Константный указатель на неконстантное значение

int val1 = 10; int val2 = 20; int* const ptr = &val1; std::cout 

В этом случае можно изменять значение, на которое указывает указатель. Но нельзя изменять сам указатель.

Кроме того указатель при объявлении нужно сразу инициализировать.

Константный указатель на константное значение

int val1 = 10; int val2 = 20; const int* const ptr = &val1; std::cout 

В этом случае нельзя менять ни указатель, ни значение, на которое он указывает.

Ссылки

Ссылка - это тип переменной в языке C++, который работает как псевдоним другого объекта или значения. При объявлении ссылки перед её именем ставится символ амперсанда &. Сама же ссылка не может быть пустой, и должна быть обязательно проинициализирована именем переменной, на которую она ссылается. Изменить значение ссылки после инициализации невозможно.

<тип> &<имя_ссылки> = <имя_переменной>[, &<имя_ссылки> = <имя_переменной>].

При создании ссылки на константную переменную, ссылка тоже должна быть создана как константная. Можно также создать константную ссылку на обычную переменную: в этом случае изменить переменную через ссылку не получится.

Любые действия со ссылкой трактуются компилятором как действия, которые будут выполняться над объектом, на который она ссылается.

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

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

int value = 123; int &refval = value; refval = 12345; std::cout 
Ссылки r-value

В стандарте C++11 ввели новый тип ссылок - ссылки r-value. Ссылки r-value - это ссылки, которые инициализируются только значениями r-values. Объявляются такие ссылки, в отличие от обычных, с помощью двух символов амперсанда &&.

<тип> &&<имя_ссылки> = <выражение r-value>[, &&<имя_ссылки> = <выражение r-value>].

Ссылки r-value, в отличие от обычных ссылок, ссылаются не на постоянный, а на временный объект, созданный при инициализации ссылки r-value.

Такие ссылки обладают двумя важными свойствами:

  • продолжительность жизни объекта, на который ссылается ссылка увеличивается до продолжительности жизни самой ссылки
  • неконстантные ссылки r-value позволяют менять значение r-values, на который они ссылаются
int &&ref = 10; ref = ref + 20; std::cout 

Ссылки r-value - позволяют избегать логически ненужного копирования и обеспечивать возможность идеальной передачи (perfect forwarding). Прежде всего они предназначены для использования в высокопроизводительных проектах и библиотеках.

  • Уголок в Вконтакте
  • Уголок в Телеграм
  • Уголок в YouTube
  • Все справочники
  • Справочник по JavaScript
  • Справочник по Ассемблеру
  • Справочник по C++
    • - Типы данных
    • - Переменные
    • - Указатели и ссылки
    • - Массивы
    • - Циклы и ветвления
    • - Перечисления
    • - Структуры и объединения
    • - Классы
    • - Наследование классов
    • - Перегрузка операторов

    Как создать ссылку на объект c

    Ссылка (reference) представляет способ манипулировать каким-либо объектом. Фактически ссылка - это альтернативное имя для объекта. Для определения ссылки применяется знак амперсанда &:

    int number ; int &refNumber ;

    В данном случае определена ссылка refNumber, которая ссылается на объект number. При этом в определении ссылки используется тот же тип, который представляет объект, на который ссылка ссылается, то есть в данном случае int.

    При этом нельзя просто определить ссылку:

    Она обязательно должна указывать на какой-нибудь объект.

    Также нельзя присвоить ссылке литеральное значение, например, число:

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

    #include int main() < int number ; int &refNumber ; std::cout 

    Изменения по ссылке неизбежно скажутся и на том объекте, на который ссылается ссылка.

    Можно определять не только ссылки на переменные, но и ссылки на константы. Но при этом ссылка сама должна быть константной:

    const int number; const int &refNumber; std::cout 
    

    Инициализировать неконстантную ссылку константным объектом мы не можем:

    const int number ; int &refNumber ; // ошибка

    Также константная ссылка может указывать и на обычную переменную, только значение по такой ссылке мы не сможем изменить:

    int number ; const int &refNumber ; std::cout 
    

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

    ссылки в цикле for

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

    #include int main() < int numbers[] ; // меняем число на его квадрат for (auto n : numbers) < n = n * n; >// смотрим результат for (auto n : numbers) < std::cout std::cout

    Здесь два цикла. В первом цикле при переборе массива помещаем каждый элемент массива в переменную n и изменяем ее значение на квадрат числа. Однако это приведет только к изменению этой переменной n, но никак не элементов перебираемого массива numbers. Элементы массива сохранят свои значения, что нам и покажет второй цикл, который выводит элементы на консоль:

    1 2 3 4 5

    Теперь используем ссылки:

    #include int main() < int numbers[] ; // теперь n - ссылка на элемент массива for (auto& n : numbers) < n = n * n; >// смотрим результат for (auto n : numbers) < std::cout std::cout

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

    1 4 9 16 25

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

    #include int main() < int numbers[] ; // n - константная ссылка for (const auto& n : numbers) < std::cout std::cout

    Хотя здесь мы не можем изменять значение элемента, но также с помощью ссылок оптимизируем перебор массива, так как элементы массива не копируются в переменную n.

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

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