Распределение памяти при передаче аргументов функции — КиберПедия 

Организация стока поверхностных вод: Наибольшее количество влаги на земном шаре испаряется с поверхности морей и океанов (88‰)...

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

Распределение памяти при передаче аргументов функции

2017-06-09 311
Распределение памяти при передаче аргументов функции 0.00 из 5.00 0 оценок
Заказать работу

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

Рассмотрим более подробно метод Add класса Complex. Изменим его немного, так, чтобы он вместо изменения состояния объекта возвращал результат операции сложения:

Complex Complex::Add(Complex x){ Complex result; result.real = real + x.real; result.imaginary = imaginary + x.imaginary; return result;}

 

При вызове этого метода

Complex n1;Complex n2;...Complex n3 = n1.Add(n2);

 

значение переменной n2 передается в качестве аргумента. Компилятор создает временную переменную типа Complex, копирует в нее значение n2 и передает эту переменную в метод Add. Такая передача аргумента называется передачей по значению. У передачи аргументов по значению имеется два свойства. Во-первых, эта операция не очень эффективна, особенно если объект сложный и требует большого объема памяти или же если создание объекта сопряжено с выполнением сложных действий (о конструкторах объектов будет рассказано в лекции 12). Во-вторых, изменения аргумента функции не сохраняются. Если бы метод Add был бы определен как

Complex Complex::Add(Complex x){ Complex result; x.imaginary = 0; // изменение аргумента метода result.real = real + x.real; result.imaginary = imaginary + x.imaginary; return result;}

 

то при вызове n3 = n1.Add(n2) результат был бы, конечно, другой, но значение переменной n2 не изменилось бы. Хотя в данном примере изменяется значение аргумента метода Add, этот аргумент – лишь копия объекта n2, а не сам объект. По завершении выполнения метода Add его аргументы просто уничтожаются, и первоначальные значения фактических параметров сохраняются.

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

Другим способом передачи аргументов является передача по ссылке. Если изменить описание метода Add на

ComplexComplex::Add(Complex& x){ Complex result; result.real = real + x.real; result.imaginary = imaginary + x.imaginary; return result;}

 

то при вызове n3 = n1.Add(n2) компилятор будет создавать ссылку на переменную n2 и передавать ее методу Add. В большинстве случаев это намного эффективнее, так как для ссылки требуется немного памяти и создать ее проще. Однако мы получим нежелательный в данном случае эффект. Метод

Complex Complex::Add(Complex& x){ Complex result; x.imaginary = 0; // изменение значения // по переданной ссылке result.real = real + x.real; result.imaginary = imaginary + x.imaginary; return result;}

 

изменит значение переменной n2. Операция Add не предусматривает изменения собственных операндов. Чтобы избежать ошибок, лучше записать аргумент с описателем const, который определяет соответствующую переменную как неизменяемую.

Complex::Add(const Complex& x)

 

В таком случае попытка изменить значение аргумента будет обнаружена на этапе компиляции, и компилятор выдаст ошибку. Передачей аргумента по неконстантной ссылке можно воспользоваться в том случае, когда функция действительно должна изменить свой аргумент. Например, метод Coord класса Figure записывает координаты некой фигуры в свои аргументы:

voidFigure::Coord(int& x, int& y){x = coordx;y = coordy;}

 

При вызове

int cx, cy;Figure fig;...fig.Coord(cx, cy);

 

переменным cx и cy будет присвоено значение координат фигуры fig.

Вернемся к методу Add и попытаемся оптимизировать передачу вычисленного значения. Простое на первый взгляд решение возвращать ссылку на результат не работает:

Complex&Complex::Add(const Complex& x){ Complex result; result.real = real + x.real; result.imaginary = imaginary + x.imaginary; return result;}

 

При выходе из метода автоматическая переменная result уничтожается, и память, выделенная для нее, освобождается. Поэтому результат Add – ссылка на несуществующую память. Результат подобных действий непредсказуем. Иногда программа будет работать как ни в чем не бывало, иногда может произойти сбой, иногда результат будет испорчен. Однако возвращение результата по ссылке возможно, если объект, на который эта ссылка ссылается, не уничтожается после выхода из функции или метода. Если метод Add прибавляет значение аргумента к текущему значению объекта и возвращает новое значение в качестве результата, то его можно записать:

Complex&Complex::Add(const Complex& x){ real += x.real; imaginary += x.imaginary; return *this; // передать ссылку на текущий объект}

 

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

x.Add(y) = z;

 

К значению объекта x прибавляется значение y, а затем результату присваивается значение z (фактически это эквивалентно x = z). Чтобы запретить подобные конструкции, достаточно добавить описатель const перед типом возвращаемого значения:

const Complex&Complex::Add(const Complex& x)...

 

Передача аргументов и результата по ссылке аналогична передаче указателя в качестве аргумента:

Complex* Complex::Add(Complex* x){ real += x->real; imaginary += x->imaginary; return this;}

 

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

Рекомендации по передаче аргументов

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

2. Если в функции или методе значение аргумента используется, но не изменяется, передавайте аргумент по неизменяемой ссылке.

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

4. Передача по указателю используется, только если функции нужен именно указатель, а не значение объекта.

 

 



Поделиться с друзьями:

Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ - конструкции, предназначен­ные для поддерживания проводов на необходимой высоте над землей, водой...

Таксономические единицы (категории) растений: Каждая система классификации состоит из определённых соподчиненных друг другу...

Археология об основании Рима: Новые раскопки проясняют и такой острый дискуссионный вопрос, как дата самого возникновения Рима...

Состав сооружений: решетки и песколовки: Решетки – это первое устройство в схеме очистных сооружений. Они представляют...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.009 с.