В PHP кто-то может объяснить клонирование против ссылки на указатель? - PullRequest
18 голосов
/ 31 августа 2010

Начнем с того, что я понимаю программирование и объекты, но следующее не имеет особого смысла для меня в PHP.

В PHP мы используем оператор &, чтобы получить ссылку на переменную. Я понимаю ссылку как способ ссылки на одну и ту же «вещь» с другой переменной. Если я скажу, например,

$b = 1;
$a =& $b;
$a = 3;
echo $b;

выведет 3, потому что изменения, внесенные в $ a, такие же, как изменения, внесенные в $ b. И наоборот:

$b = 1;
$a = $b;
$a = 3;
echo $b;

должен вывести 1.

Если это так, зачем нужно ключевое слово clone? Мне кажется, что если я установлю

$obj_a = $obj_b тогда изменения, внесенные в $ obj_a, не должны влиять на $ obj_b, и наоборот, $ obj_a = & $ obj_b должен указывать на тот же объект, поэтому изменения, внесенные в $ obj_a, влияют на $ obj_b.

Однако в PHP кажется, что некоторые операции с $ obj_a DO влияют на $ obj_b, даже если они назначены без оператора ссылки ($obj_a = $obj_b). Это вызвало у меня сегодня неприятную проблему при работе с объектами DateTime, которые я в итоге исправил, выполнив в основном:

$obj_a = clone $obj_b

Но большая часть кода php, который я пишу, не требует явного клонирования, как в этом случае, и прекрасно работает без него. Что тут происходит? И почему PHP должен быть таким неуклюжим ??

Ответы [ 3 ]

44 голосов
/ 31 августа 2010

В сущности, в PHP есть два способа работы с переменными ...

Для всего, кроме объектов:

  1. Назначение по значению (то есть, если вы делаете копию $a = $b.
  2. Ссылка может быть достигнута с помощью $a = &$b (обратите внимание, что оператор ссылки работает с переменной, а не с оператором присваивания, поскольку вы можете использовать его в других местах) ...
  3. При копировании используется техника копирования при записи. Так что если вы сделаете $a = $b, в памяти не будет копии переменной. Но если вы выполните $a = 5;, память будет скопирована и перезаписана.

Для объектов:

  1. Назначение по ссылке на объект. Это не то же самое, что нормальная переменная по ссылке (я объясню почему позже).
  2. Копировать по значению можно, выполнив $a = clone $b.
  3. Ссылка может быть достигнута с помощью $a = &$b, но имейте в виду, что это не имеет никакого отношения к объекту. Вы связываете переменную $a с переменной $b. Неважно, объект это или нет.

Итак, почему присвоение объектам не является референсным? Что произойдет, если вы сделаете:

$a = new stdclass();
$b = $a;
$a = 4;

Что такое $b? Ну, это stdclass ... Это потому, что это не запись ссылки на переменную, а на объект ...

$a = new stdclass();
$a->foo = 'bar';
$b = $a;
$b->foo = 'baz';

Что такое $a->foo? Это baz. Это потому, что когда вы сделали $b = $a, вы говорите PHP использовать тот же экземпляр объекта (отсюда и ссылку на объект). Обратите внимание, что $a и $b не являются одной и той же переменной, но они оба ссылаются на один и тот же объект.

Один из способов думать об этом - думать о всех переменных, которые хранят объект, как о сохранении указателя на этот объект. Таким образом, объект живет где-то еще. Когда вы назначаете $a = $b, где $b - объект, все, что вы делаете, это копируете этот указатель. Фактические переменные все еще не пересекаются. Но когда вы делаете $a = &$b, вы сохраняете указатель на $b внутри $a. Теперь, когда вы манипулируете $a, он соединяет цепочку указателей с базовым объектом. Когда вы используете оператор clone, вы говорите PHP скопировать существующий объект и создать новый с тем же состоянием ... Так что clone на самом деле просто делает копию по значению varaible ...

Так что, если вы заметили, я сказал, что объект не хранится в фактической переменной. Он хранится где-то еще, и в переменной ничего не хранится, кроме указателя. Таким образом, это означает, что вы можете иметь (и часто имеете) несколько переменных, указывающих на один и тот же экземпляр. По этой причине внутреннее представление объекта содержит refcount (просто подсчет количества переменных, указывающих на него). Когда значение refcount объекта падает до 0 (это означает, что все переменные, указывающие на него, либо выходят из области видимости, либо заменяются другими значениями), он собирается для сбора (так как он более недоступен) ...

Подробнее о ссылках и PHP вы можете прочитать в документации ...

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

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

В общем, я не так часто использую ссылки на переменные. Редко я нахожу в них искреннюю потребность. Но я все время использую ссылки на объекты. Я использую их так много, что я счастлив, что они по умолчанию. В противном случае мне нужно написать какой-нибудь оператор (так как & обозначает ссылку на переменную, должен быть другой, чтобы обозначить ссылку на объект). И, учитывая, что я редко использую clone, я бы сказал, что в 99,9% случаев использования должны использоваться ссылки на объекты (поэтому используйте оператор для более низких частот) ...

JMHO

Я также создал видео, объясняющее эти различия. Проверьте это на YouTube .

4 голосов
/ 31 августа 2010

Вкратце:

В PHP 5+ объекты передаются по ссылке.В PHP 4 они передаются по значению (поэтому во время выполнения он передавался по ссылке, что устарело).Итак, у вас есть для использования оператора clone в PHP5 для копирования объектов:

$objectB = clone $objectA;

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

2 голосов
/ 12 ноября 2014

Я написал презентацию, чтобы лучше объяснить, как php управляет памятью с ее переменными:

https://docs.google.com/presentation/d/1HAIdvSqK0owrU-uUMjwMWSD80H-2IblTlacVcBs2b0k/pub?start=false&loop=false&delayms=3000

взгляни;)

...