Какие способы существуют для передачи объектов функциям

Передача объекта в Функцию — Переменная ссылается на объект

Продолжим практическое освоение Javascript . Поговорим о передаче объектов в функции.

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

Что нужно знать.

1. Читайте теорию об Объектах (перейти к ней можно по выше приведенной ссылке).

2. Полезно будет разобраться с материалом о передаче переменных в функцию.

Передача объектов в функцию — Немного теории

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

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

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

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

Переменная — это ссылка на объект, она на него указывает.

В функцию передается копия ссылки на объект.

Для тех, кто запутался — это нужно прочесть и прокрутить в голове несколько раз.

Пример передачи объекта в функцию

1. В качестве примера рассмотрим объект, который содержит свойства, отражающие какие-либо качества и параметры собаки.

/* Объект «Собака» */
var
juicy = <
name : «Джуси» ,
/* имя собаки */
weight : 15 , /* её вес в кг. */
breed : «Смесь» , /* порода */
loves : «Прогулка» /* любит гулять */
>

— переменная juicy является ссылкой на объект, описывающий характеристики собаки;

2. Напишем функцию для снижения веса собаки .

function lose_Weight( dog , value )
<
dog .weight = dog .weight — value ;
>

Функция lose_Weight работает со свойством weight (вес) объекта «Собака» и содержит два параметра dog и value:

— в параметр dog следует передать переменную juicy (см. выше), как ссылку на объект;

— в параметр value заносится произвольное число.

Итак, если в параметр dog функции lose_Weight передать переменную juicy , которая ссылается на объект «Собака», то функция снизит вес собаки на указанное во втором параметре число.

Собака Джуси сейчас весит 12 килограмм

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

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

Мы вызвали функцию lose_Weight с параметрами juicy и 3 .

juicy — это переменная, которая ссылается на объект «Собака»;

Внутри функции происходит обращение к свойству объекта weight — это вес собаки, из значения которого (то есть из 15) вычитается число 3 .
dog.weight = dog.weight — value

В результате изменения , которые были сделаны внутри функции , отразились на исходном объекте .

Ведь далее, при выводе на экран свойства weight объекта, мы видим, что собака стала весить 12 кг.

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

Источник

Для чего нужна передача объекта в функцию по значению и по указателю в С++?

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

Читайте также:  Способы передвижения моллюсков с примерами

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

По указателю только 1 пример знаю, когда передается указатель на начало массива — тогда указателем можно орудовать как итератором, например.
Также нужно если в функции нужно менять значение указателя — тогда передается указатель на указатель. Хотя вещь сомнительная, т.к. если указатель изменится, то предыдущий адрес окажется утерянным. И соответственно старый объект может быть потерян, как и память, выделенная под него.
А другие применения на практике в хороших больших серьезных проектах какое?

По значению вообще не понимаю когда нужно если есть «const &».

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

  • Вопрос задан более трёх лет назад
  • 2503 просмотра

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

Например, поиск наибольшего общего делителя:

В функции изменяются копии переданных объектов, а не сами объекты.

Upd.1
Указатели используются в многих структурах данных. Например, связный список:

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

Upd.2
Можно создавать указатели на функции. Функцию нельзя передать по значению или по ссылке.

да я понимаю эти базовые вещи. мне не понятно зачем «по значению» когда можно «по const ссылке». при этом же данные также неизменяемы.

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

По поводу указателя — опять же я спрашивал не вообще зачем такой элемент. А зачем на практике нужна передача указателя ведь по сути передается опять же ссылка на объект и если в функцию нужно передать 1 объект, то указатель особо смысла нету передавать, т.к. это равноценно передачи по ссылке (поправьте если не прав).
Также единственная догадка, когда это может понадобится: если нужно передать указатель на указатель, т.е. когда в функции нужно менять значение указателя.
Ну это помимо того когда передается указатель на часть массива и используется в алгоритме функции в качестве итератора.

Я очень новичок, поэтому поправляйте если я где-то не так понимаю.

Источник

Объектно-ориентированное программирование

10.3 Создание и удаление объектов

10.3.1 Присваивание объектов, передача в функцию и возвращение объекта

Можно заметить, что в ряде случаев (например, при перегрузке операторов) мы использовали передачу объекта в функцию и возвращение не по значению, а с использованием ссылки. Для этого существует достаточно веская причина: передача объектов по значению, равно как и их присваивание, может приводить к нежелательным последствиям.

Читайте также:  Возмещение причиненного вреда как способ защиты гражданских прав

Один объект можно присвоить другому, если оба объекта имеют одинаковый тип (если объекты имеют разные типы, то компилятор выдаст сообщение об ошибке). По умолчанию, когда объект A присваивается объекту B , то осуществляется побитовое копирование всех элементов-данных A в соответствующие элементы-данные B . Именно это копирование и является потенциальным источником проблем. Особенно внимательным нужно быть при присваивании объектов, имеющих свойства-указатели.

Рассмотрим в качестве примера класс matrix , хранящий в себе прямоугольную матрицу из элементов типа double . Размерность матрицы будет передаваться конструктору класса, после чего будет выполняться динамическое выделение памяти под нужное количество элементов. В классе будут также предусмотрены методы get_val() чтобы получить элемент матрицы с индексами (i,j) и set_val() чтобы установить в заданный элемент новое значение.

Однако присвоив просто так одну переменную типа matrix другой, мы не сможем избежать побочных эффектов.

При запуске программа выдаёт сообщение «a[0,0] = 200; b[0,0] = 200» вместо ожидаемого «a[0,0]=100» , после чего и вовсе аварийно завершается с сообщением о попытке дважды освободить память. На самом деле это происходит по вполне очевидной причине. При побитовом копировании скопировался адрес указателя m, а не содержимое блока памяти, динамически выделенного по этому адресу. В результате оба объекта получают указатель на одну и ту же последовательность вещественных чисел.

Аналогично присваиванию, объекты можно передавать в функции в качестве аргументов, в точности так, как передаются данные других типов. Однако следует помнить, что в C++ по-умолчанию параметры передаются по значению. Это означает, что внутри функции (а точнее, в стеке) создаётся копия объекта-аргумента, и эта копия, а не сам объект, будет далее использоваться функцией. Благодаря этому функции могут произвольно изменять переданные значения, не влияя на оригинал.

Итак, при передаче объекта в функцию создаётся новый объект, а когда работа функции завершается, копия переданного объекта будет разрушена. Как всегда при разрушении объектов, при этом будет вызван деструктор копии. И здесь может наблюдаться очередной побочный эффект: если переданный в качестве параметра объект содержит в себе указатель на динамически выделенную область памяти, деструктор копии её освободит. Но так как копия создавалась побитовым копированием, деструктор копии высвободит область памяти, на которую указывал объект-оригинал. Исходный объект будет по-прежнему «видеть» свои данные по указанному адресу, однако для системы эта память будет считаться свободной. Рано или поздно она будет выделена какому-то другому объекту, и данные окажутся затёрты.

Кроме возможности преждевременного разрушения объекта-оригинала, к аварийной ситуации приведёт вызов его деструктора (в конце работы программы или при выходе из соответствующей области видимости), который попытается освободить уже свободную память. Та же проблема возникает при использовании объекта в качестве возвращаемого значения.

Во всех трёх случаях (при присваивании объекта, при использовании его как параметра и при передаче в качестве возвращаемого значения) если деструктор высвобождает динамически выделенную память, то разрушение временного объекта приведёт к преждевременному разрушению данных оригинала.

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

Любой конструктор копирования имеет следующую форму:

Читатель должен помнить, что в таком описании &obj — это ссылка на объект, известная ещё как скрытый указатель.

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

Во второй строке примера переменная p3 и объявляется и определяется, а в третьей строке переменной p1 всего лишь присваивается значение. Иными словами, конструктор копирования вызывается для конкретной переменной за время её жизни только один раз, а присваивать значения ей можно многократно. В логике работы конструктора копирования и оператора присваивания настолько много общего, что часто рекомендуют описывать одну операцию в терминах другой. Фактически операция присваивания неявно используется в конструкторе копирования. Однако конструктор копирования может добавлять дополнительные действия по инициализации переменных в довесок к тем действиям, которые должен выполнять оператор присваивания.

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

Если оператор присваивания для класса не был определён, то в случае необходимости (если для объектов этого класса в тексте программы выполняется присваивание) компилятор автоматически генерирует оператор присваивания по умолчанию, выполняющий то самое побитовое копирование объекта.

10.3.2 Подробнее об указателях и ссылках на объекты

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

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

По этой причине в C++ был введён специальный тип данных — ссылка или скрытый указатель. На понятийном уровне ссылку можно воспринимать как другое имя (псевдоним) переменной. Фактически же это указатель на переменную, который выглядит так, как будто к переменной обращаются по значению: программист объявляет такую ссылку, присваивает ей какую-либо переменную и далее пользуется ссылкой как ещё одной переменной. Компилятор же сам автоматически подставляет ко всем обращениям к ссылке операции адресации и разадресации.

Удобнее всего использовать ссылки для передачи параметров и возвращаемых значений.

Напомним: ссылка объявляется так же как указатель, только с использованием знака «» вместо «звёздочки». Сравним, как выглядит код при передаче аргумента по указателю и по ссылке, на примере функции zero() , устанавливающей в ноль координаты переданного ей объекта класса point :

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

Часто ссылки применяют в сочетании с указателем this . Рассмотрим в качестве примера переопределение оператора присваивания для класса point :

В объявлении функции мы указали ссылочный тип в качестве как аргумента, так и возвращаемого значения. Оператор присваивания должен возвращать результат операции, чтобы стало возможным каскадное присваивание наподобие a=b=c=0 . В качестве возвращаемого значения мы указываем разадресованный указатель this, однако возвращён в качестве результата будет тот объект, который вызывал операцию «=» , а не его копия.

Приведём модифицированный вариант класса matrix , имеющий как конструктор копирования, так и оператор присваивания, и выдающий на экран правильный результат.

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

Источник

Оцените статью
Разные способы