- Передача объекта в Функцию — Переменная ссылается на объект
- Передача объектов в функцию — Немного теории
- Пример передачи объекта в функцию
- Для чего нужна передача объекта в функцию по значению и по указателю в С++?
- Объектно-ориентированное программирование
- 10.3 Создание и удаление объектов
- 10.3.1 Присваивание объектов, передача в функцию и возвращение объекта
- 10.3.2 Подробнее об указателях и ссылках на объекты
Передача объекта в Функцию — Переменная ссылается на объект
Продолжим практическое освоение 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 . На самом деле это вполне естественно. Вспомним: переменные, объявленные в закрытой секции класса, доступны только для методов этого же класса (а не «этого же объекта»). Иными словами, объекты одного класса могут получать доступ к закрытым членам друг друга, хотя используется это не так часто.
Источник