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

Как вернуть ссылку из функции c

  • автор:

Возвращаемые значения функции ссылочного типа

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

  • Возвращаемая информация представляет собой настолько крупный объект, что возврат ссылки является более эффективным, чем возврат копии.
  • Тип функции должен представлять собой l-значение.
  • Объект, на который указывает ссылка, не выйдет из области видимости при возврате управления функцией.

Так же, как это может быть более эффективным для передачи больших объектов в функции по ссылке, он также может быть более эффективным для возврата больших объектов из функций по ссылке. Протокол возврата ссылок устраняет необходимость копировать объект во временное хранилище перед возвратом.

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

Пример

Рассмотрим пример Point .

// refType_function_returns.cpp // compile with: /EHsc #include using namespace std; class Point < public: // Define "accessor" functions as // reference types. unsigned& x(); unsigned& y(); private: // Note that these are declared at class scope: unsigned obj_x; unsigned obj_y; >; unsigned& Point :: x() < return obj_x; >unsigned& Point :: y() < return obj_y; >int main() < Point ThePoint; // Use x() and y() as l-values. ThePoint.x() = 7; ThePoint.y() = 9; // Use x() and y() as r-values. cout

Выходные данные

x = 7 y = 9 

Обратите внимание, что функции x и y объявляются как типы возвращаемых ссылок. Эти функции можно использовать на любой стороне оператора присваивания.

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

Объявления ссылочных типов должны содержать инициализаторы. Исключение составляют следующие случаи.

  • Явное extern объявление
  • Объявление члена класса
  • Объявление в классе
  • Объявление аргумента в адрес функции или типа возвращаемого значения для функции

Предупреждение при возвращении адреса локальной переменной

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

// C4172 means Don't do this. Foo& GetFoo() < Foo f; . return f; >// f is destroyed here 

Компилятор выдает предупреждение в этом случае: warning C4172: returning address of local variable or temporary В простых программах доступ может случайно сохраниться, если ссылка будет использована вызывающим объектом до перезаписи соответствующей области памяти. Однако это чистая случайность. Обратите внимание на предупреждение.

Возврат ссылок

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

#include
char &replace(int i) ; // возврат ссылки
char s [80] = "Hello There";
int main()
replace(5) = 'X'; // присвоение X пробелу после Hello
cout return 0;
>
char &replace(int i)
return s [ i ];
>

Эта программа заменяет пробел между словами «Hello» и «There» символом «X». В результате программа выводит на экран «HelloXThere».

Функция replace() в соответствии со своим объявлением возвращает ссылку на символьный мас­сив. В соответствии со своей реализацией функция replace() возвращает ссылку на элемент масси­ва s, определяющийся аргументом i. Далее возвращаемая функцией replace() ссылка используется функцией main() для присвоения элементу буквы «X».

Возврат из функции объекта по ссылке

Теперь в main() создаю массив объектов типа Stock и присваиваю указателю top значение одного из элементов массива:

for (int st = 1; st < STKS; st++) < top = &top ->TopVal(stocks[st]); > 

Объясните, почему при присваивании указателю top значения из функции TopVal следует указывать знак ссылки, ведь из функции уже передается ссылка?

Отслеживать
44.8k 3 3 золотых знака 39 39 серебряных знаков 90 90 бронзовых знаков
задан 16 дек 2016 в 13:53
Misha Gorshenin Misha Gorshenin
3 2 2 бронзовых знака

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

В этом предложении

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

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

Итак, в данном объявлении функции- члена класса

const Stock & Stock::TopVal(const Stock & _st) const ^^^ ^^^ < if (_st.total_val >total_val) return _st; else return *this; > 

символ & используется для объявления ссылок. Функция возвращает ссылку на объект типа const Stock , а также объявляет в качестве параметра ссылку на объект такого эе типа const Stock .

А в этом предложении

указателю top , который скорей всего объявлен как

const Stock *top; 

присваивается адрес того объекта, ссылка на который возвращается из функции.

Чтобы было более понятно, то рассмотрите следующий пример

int x = 10; int &r = x; int *p = &x; r = 20; *p = 30; 

В этом примере объявляется ссылка r на объект x и указатель p , который будет содержать адрес объекта x . Ссылку можно рассматривать как альтернативное имя объекта - как алиас объекта. То еть к области памяти, где расположен объект x , вы можете обращаться по исходному имени x , или используя имя r .

Отслеживать
ответ дан 16 дек 2016 в 13:59
Vlad from Moscow Vlad from Moscow
44.8k 3 3 золотых знака 39 39 серебряных знаков 90 90 бронзовых знаков

Из функции передается ссылка, но ссылка - это как бы псевдоним, а не адрес. Т.е. если у вас есть

int x; int& r = x; int* p = &x; 

то r - обратите внимание, инициализируется x , а не адресом, как указатель. Как именно внутренне реализована ссылка - в данном случае не играет роли.

Вы используете r вместо x - например, в присваивании r = 5; . B точно так же вы получаете адрес через ссылку - p = &x тождественно p = &r; .

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

Отслеживать
ответ дан 16 дек 2016 в 14:26
222k 15 15 золотых знаков 120 120 серебряных знаков 234 234 бронзовых знака

В книге Стенли Б. Липпман, Жози Лажойе, Барбара Э. Му. Язык программирования C++. Базовый курс. Пятое издание. 2017 в главе 15 рассматривается иерархия классов Quote - Disc_quote - Bulk_quote.

У меня возникла необходимость передать в некоторую функцию ссылки объекты базового или производных классов и вернуть из этой функции также ссылки на эти объекты.

Приведенный на этой странице пример не срабатывает:

auto ref_return(Quote& ref_obj) < return ref_obj; >; // . Bulk_quote bulk_quote; auto b_q = ref_return(bulk_quote); // Возвращает не ссылку, а объект. // У меня выстрелили вот такие конструкции: // Определение фунции, которая должна возвратить обычную ссылку: auto ref_return(Quote& ref_obj) -> Quote& < return ref_obj; >; // Вызов должен быть таким: decltype(auto) any_obj_ref = ref_return(bulk_quote); // Такая же проблема была и с лямбда, и примерно так же она решилась: // Механизм возврата простой ссылки из лямда-функции: // Лямбда выражение должно быть таким: auto ret_derived = [](Quote& any_quote_obj) -> Quote& ; Quote any_quote_obj - объект базового класса // Вызов должен быть таким: decltype(auto) bulk_quote_obj = ret_derived(bulk_quote); 

Может быть, кому-нибудь окажется полезной эта информация.

Для чего нужен возврат значения по ссылке?

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

#include int& func(int &a) < std::cout int main()

Я думал, что выведется три одинаковых адреса, но это не так. Если сделать возврат по значению, то результат будет идентичный. Для чего тогда вообще нужен возврат значения по ссылке?
Если мы возвращаем указатель на переменную, то в caller-е она имеет тот же адрес (мы его и вернули) и информацию по этому адресу, однако адрес указателя уже будет другим. Если мы возвращаем переменную по ссылке, то адрес у этой переменной в caller-е уже другой, такой же остаётся только информация. Почему и зачем?

  • Вопрос задан более двух лет назад
  • 336 просмотров

1 комментарий

Простой 1 комментарий

Евгений Шатунов @MarkusD Куратор тега C++
Владимир Коршунов , а при чем тут адреса если вопрос задан о ссылках?
Решения вопроса 1
Wataru @wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.

Вот тут вы взяли ссылку, которую вернула func и скопировали значение в переменную b. Поэтому ниже &b выведет вам адрес переменной b, а не то, что вы хотели.

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

Ответ написан более двух лет назад
Нравится 2 3 комментария
Kevsh @FaulerAffe Автор вопроса

int& b = func(a);
Если сделать так, то выводится три одинаковых адреса. Я не очень понимаю, что такое b. По идее это адрес, но записать в него указатель мы не можем, можем только переменную, при этом мы получим полную копию этой переменной – от адреса до значения. То есть получается, если мы хотим сделать так, чтобы новая переменная ссылалась на ту же память, что и уже созданная переменная, то мы ставим &, а если хотим создать копию, но по другому адресу, то не ставим?

Wataru @wataru Куратор тега C++
Владимир Коршунов, int& b = . - это ссылка, которая инициализируется ссылкой, возвращенной func.

Владимир Коршунов, int& b = func(a);
b - ссылка.
Ссылка это не указатель!
Если копнуть в ассемблер, то ссылки - это те же указатели (т.к. ни каких ссылок в ассемблере нет), но на уровне С++ ссылка это не указатель.

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

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

На счет разъименования - эта операция нужна для указателей, т.к. вы можете работать либо с самим указателем, либо с объектом на который указывает указатель (тут и нужно разъименование).
С самой ссылкой вы ничего дополнительно сделать не можете. Вы можете только создать ее и уничтожить (при выходе из области видимости). Все остальные операции с участием ссылки происходят реально над объектом, на который ссылается ссылка, поэтому нужды в разъименовании нет.

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

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