Перейти к содержимому

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

  • автор:

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

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

#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».

Как работает возврат ссылки?

Что-то я туплю. Есть класс буфера, который, возвращая элемент, копирует его в свой член OutObject и возвращает ссылку на OutObject. Принимается же этот элемент в объект:

struct DataStr; DataStr& TBuffer::get( void ) < // . OutObject = *OldElement; return OutObject; >DataStr data; data = buffer->get(); 

К какой области памяти теперь относится data? К той же, что и раньше? Или к OutObject? Или здесь все же происходит копирование значения?

Отслеживать
задан 27 дек 2012 в 6:26
137 2 2 золотых знака 2 2 серебряных знака 9 9 бронзовых знаков

— При выполнении метода get для OutObject будет вызван деструктор и operator=(*OldElement) , определенный явно или неявно. — После выполнения этого кода для data будет вызван оператор присваивания с параметром-ссылкой на результат метода get .

27 дек 2012 в 10:16
Не понял, какой еще деструктор? Получается, ссылку возвращать бесполезно? Можно просто объект?
27 дек 2012 в 11:26

@ProkletyiPirat В строчке DataStr data; будет вызван конструктор для без параметров DataStr::DataStr() . В том случае, если этот вызов можно доказуемо соптимизировать, то, наверно, этого вызова не произойдет, но потенциально он имеет место быть. @cyrax Вообще, метод какой-то странный и путающий. Неконстантный getter, который содержит какую-то логику — это всегда плохо. Не думайте о количестве копирований (поскольку есть RVO и прочие полезные штуки), лучше задумайтесь о том, как сделать метод get константным и прозрачным. Возвращать объект можно и по значению.

27 дек 2012 в 12:53

Я лично так и не понял какого типа переменные OutObject и OldElement . Без этого дальнейший разговор бессмысленен.

28 дек 2012 в 21:21

@cyrax, Вы на самом деле чего хотите? Хранить данные в памяти экземпляра объекта и обращаться к этой памяти? Тогда так и код напишите. Возвращайте указатель на эти байты и не морочьте (прежде всего себе самому) голову. Если хотите копировать, тогда явно (например, вызвав memcpy()) копируйте. Делайте просто и понятно. Про KISS принцип слышали?

29 дек 2012 в 6:07

1 ответ 1

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

о боже, ответ получился безумно длинным и без BBcode выглядит ужасно нечитабельно :((

не пугайтесь огромного размера :))

предисловие

чтобы не было путаницы ввиду пару обозначений $ //некое место в коде (может иметь номер $1,$2) переменная //именованная область данных (переменная с именем) ^ // временная переменная без имени но с адресом в памяти адрес_объекта //адрес в памяти какого-нибудь объекта адрес_переменной //адрес какой то переменной переменная_указатель //переменная типа указатель переменная_ссылка //переменная типа ссылка переменная_объект //переменная типа объект int переменная_объект //переменная типа int int переменная_указатель //указатель на int int переменная_ссылка //ссылка на int (текст) //какие то мысли или пояснение 

что такое ссылка ?

все ссылки это по сути константные указатели (не путать с указателем на конст)

тип * const имя_переменной_указатель 

у ссылок есть пара отличий от просто указателя

  1. нельзя менять адрес_объекта
  2. адрес_объекта не может быть равным нулю
  3. тип ссылки и объекта должен быть одинаковым
  4. адрес_объекта можно назначить только во время создания ссылки

во время компиляции, компилятор автоматически переделает ссылку в конст_переменная_указатель , возьмёт адрес_объекта и во всех местах где используется ссылка подставит разыменование ( * конст_переменная_указатель ).

передача\возврат значений в\из функцию

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

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

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

создаём в функции новые переменные

—————пример1:
void fun(тип переменная_1_объект) $ < \\. >\\--------------------------------\\ fun(переменная_объект); 

при вызове fun() в месте $ будет выполнена операция

тип переменная_1_объект = переменная_объект 
—————пример2:
void fun(тип &переменная_1_ссылка) $ < \\. >\\--------------------------------\\ fun(переменная_объект); 

при вызове fun() в месте $ будет выполнена операция

тип &переменная_1_ссылка = переменная_объект 
—————пример3: (работать не будет)
void fun(тип *переменная_1_указатель) $ < \\. >\\--------------------------------\\ fun(переменная_объект); 

при вызове fun() в месте $ будет выполнена операция

тип *переменная_1_указатель = переменная_объект //вот тут то и неработает 

создаём в вызвавшей_функции переменные без имени «^»

—————пример1:
тип fun() < \\. return переменная_1_объект; >\\--------------------------------\\ $ fun(); 

в вызвавшей_функции в месте $ будет выполнена операция

тип ^_объект = переменная_1_объект 
—————пример2:

в вызвавшей_функции в месте $ будет выполнена операция

тип &^_ссылка = переменная_1_объект 
—————пример3: (работать не будет)
тип* fun() < \\. return переменная_1_объект; >\\--------------------------------\\ $ fun(); 

в вызвавшей_функции в месте $ будет выполнена операция

тип *^_указатель= переменная_1_объект //вот тут то и неработает 

смешиваем

—————пример:

в функции_fun в месте $1 будет выполнена операция

тип &переменная_1_ссылка = переменная_объект 

затем в вызвавшей_функции в месте $2 будет выполнена операция

тип &^_ссылка = (переменная_1_ссылка) 

тоесть ^_ссылка и переменная_1_ссылка будут указывать на переменная_объект

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

в функции_fun в месте $1 будет выполнена операция

тип * const переменная_1 = &переменная //(объект) 

затем в вызвавшей_функции в месте $2 будет выполнена операция

тип * const ^ = &(*переменная_1) // 

p.s стоит ли писать про то когда вызывается конструктор_копирования , а когда оператор= .

p.s.s вопросы, недочёты, ошибки и т.д. писать в коментах под этим постом

p.s.s.s пусть «очень много «пожеланий»» создателей Markdown

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

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

  • Возвращаемая информация представляет собой настолько крупный объект, что возврат ссылки является более эффективным, чем возврат копии.
  • Тип функции должен представлять собой 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 В простых программах доступ может случайно сохраниться, если ссылка будет использована вызывающим объектом до перезаписи соответствующей области памяти. Однако это чистая случайность. Обратите внимание на предупреждение.

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

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

// пример некорректного возвращения значения int* max(int a, int b) < if (a >b) return &a; else return &b; >

Параметры функции аналогичны переменным - при вызове функции в стеке для них выделяется память. И вданном случае возвращается адрес участка памяти соответствующего параметра ( return &a или return &b ). Но после возвращения адреса функция завершает свою работу, соответствующие участки памяти очищаются, параметры удаляются, поэтому возвращенный адрес будет недействительным. И хотя компилятор даже может скомпилировать данную функцию, ограничившись предупреждениями, но такая функция не будет работать корректно.

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

#include int* max(int*, int*); int main() < int n; int m; int* ptr = max(&n, &m); std::cout // пример корректного возвращения значения int* max(int *a, int *b) < if (*a >*b) // разыменовываем указатели return a; else return b; >

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

При этом нам необязательно присваивать результат переменной или константе, можно напрямую обратиться к результату функции:

int main() < int n; int m; std::cout 

Возвращение ссылки

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

// пример некорректного возвращения ссылки int& max(int a, int b) < if (a >b) return a; else return b; >

Здесь функция возвращает ссылку на максимальное значение - один из переданных параметров. Но поскольку память, выделенная для параметров, передаваемых по значению, после выполнения функции очищается, то возвращенная ссылка в итоге будет указывать на несуществующий объект.

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

#include int& max(int&, int&); int main() < int n; int m; int result = max(n, m); std::cout // пример корректного возвращения ссылки int& max(int& a, int& b) < if (a >b) return a; else return b; >

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

#include const int& max(const int&, const int&); int main() < int n; int m; int result = max(n, m); std::cout // пример корректного возвращения ссылки const int& max(const int& a, const int& b) < if (a >b) return a; else return b; >

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

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