在 PHP 中,有人可以解释克隆 vs 指针引用吗?

2022-01-05 00:00:00 reference clone php

首先,我了解编程和对象,但以下内容对我来说在 PHP 中没有多大意义.

To begin with, I understand programming and objects, but the following doesn't make much sense to me in PHP.

在 PHP 中,我们使用 &运算符来检索对变量的引用.我将引用理解为使用不同变量引用相同事物"的一种方式.如果我说例如

In PHP we use the & operator to retrieve a reference to a variable. I understand a reference as being a way to refer to the same 'thing' with a different variable. If I say for example

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

将输出 3,因为对 $a 所做的更改与对 $b 所做的更改相同.相反:

will output 3 because changes made to $a are the same as changes made to $b. Conversely:

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

应该输出1.

如果是这样,为什么需要 clone 关键字?在我看来,如果我设置

If this is the case, why is the clone keyword necessary? It seems to me that if I set

$obj_a = $obj_b 那么对 $obj_a 所做的更改不应影响 $obj_b,相反 $obj_a =&$obj_b 应该指向同一个对象,因此对 $obj_a 所做的更改会影响 $obj_b.

$obj_a = $obj_b then changes made to $obj_a should not affect $obj_b, conversely $obj_a =& $obj_b should be pointing to the same object so changes made to $obj_a affect $obj_b.

然而,在 PHP 中,$obj_a 上的某些操作似乎会影响 $obj_b,即使在没有引用运算符 ($obj_a = $obj_b) 的情况下进行赋值.今天在使用 DateTime 对象时,这给我带来了一个令人沮丧的问题,我最终通过以下方式解决了这个问题:

However it seems in PHP that certain operations on $obj_a DO affect $obj_b even if assigned without the reference operator ($obj_a = $obj_b). This caused a frustrating problem for me today while working with DateTime objects that I eventually fixed by doing basically:

$obj_a = clone $obj_b

但是我编写的大部分 php 代码似乎并不需要像这种情况下的显式克隆,并且没有它也能正常工作.这里发生了什么?为什么 PHP 必须如此笨重??

But most of the php code I write doesn't seem to require explicit cloning like in this case and works just fine without it. What's going on here? And why does PHP have to be so clunky??

推荐答案

基本上,PHP 中的变量有两种工作方式...

Basically, there are two ways variables work in PHP...

对于除对象之外的所有内容:

For everything except objects:

  1. 赋值是按值分配的(意味着如果你执行 $a = $b 就会产生一个副本.
  2. 引用可以通过执行 $a = &$b 来实现(注意引用运算符对变量进行操作,而不是赋值运算符,因为您可以在其他地方使用它)...
  3. 副本使用写时复制技术.因此,如果您执行 $a = $b,则没有该变量的内存副本.但是如果你执行 $a = 5;,内存会被复制并覆盖.
  1. Assignment is by value (meaning a copy occurs if you do $a = $b.
  2. Reference can be achieved by doing $a = &$b (Note the reference operator operates upon the variable, not the assignment operator, since you can use it in other places)...
  3. Copies use a copy-on-write tehnique. So if you do $a = $b, there is no memory copy of the variable. But if you then do $a = 5;, the memory is copied then and overwritten.

对于对象:

  1. 分配是通过对象引用进行的.它与引用的普通变量实际上并不相同(我稍后会解释原因).
  2. 通过执行$a = clone $b可以实现按值复制.
  3. 引用可以通过$a = &$b来实现,但要注意这与对象无关.您将 $a 变量绑定到 $b 变量.它是不是对象并不重要.
  1. Assignment is by object reference. It's not really the same as normal variable by reference (I'll explain why later).
  2. Copy by value can be achieved by doing $a = clone $b.
  3. Reference can be achieved by doing $a = &$b, but beware that this has nothing to do with the object. You're binding the $a variable to the $b variable. It doesn't matter if it's an object or not.

那么,为什么对象的赋值并不是真正的引用?如果你这样做会发生什么:

So, why is assignment for objects not really reference? What happens if you do:

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

什么是$b?好吧,它是 stdclass... 那是因为它不是在写入对变量的引用,而是对对象...

What's $b? Well, it's stdclass... That's because it's not writing a reference to the variable, but to the object...

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

什么是$a->foo?这是baz.那是因为当您执行 $b = $a 时,您是在告诉 PHP 使用相同的对象实例(因此是对象引用).请注意,$a$b 不是同一个变量,但它们都引用了同一个对象.

What's $a->foo? It's baz. That's because when you did $b = $a, you are telling PHP to use the same object instance (hence the object reference). Note that $a and $b are not the same variable, but they do both reference the same object.

一种思考方式,是将存储对象的所有变量视为存储指向该对象的指针.所以这个对象住在其他地方.当您分配 $a = $b 其中 $b 是一个对象时,您所做的就是复制该指针.实际变量仍然不相交.但是当你执行 $a = &$b 时,你在 $a 中存储了一个指向 $b 的指针.现在,当您操作 $a 时,它会将指针链级联到基础对象.当您使用 clone 运算符时,您是在告诉 PHP 复制现有对象,并创建一个具有相同状态的新对象......所以 clone 实际上只是做了一个变量的按值复制...

One way of thinking about it, is to think of all variables which store an object as storing the pointer to that object. So the object lives somewhere else. When you assign $a = $b where $b is an object, all you're doing is copying that pointer. The actual variables are still disjoint. But when you do $a = &$b, you're storing a pointer to $b inside of $a. Now, when you manipulate $a it cascades the pointer chain to the base object. When you use the clone operator, you're telling PHP to copy the existing object, and create a new one with the same state... So clone really just does a by-value copy of the varaible...

所以如果你注意到,我说对象没有存储在一个实际的变量中.它存储在其他地方,而变量中只存储了一个指针.所以这意味着您可以拥有(并且经常拥有)多个指向同一个实例的变量.出于这个原因,内部对象表示包含一个 refcount(只是指向它的变量数量的计数).当一个对象的引用计数下降到 0 时(意味着所有指向它的变量要么超出范围,要么被更改为其他东西)它被垃圾收集(因为它不再可访问)...

So if you noticed, I said the object is not stored in an actual variable. It's stored somewhere else and nothing but a pointer is stored in the variable. So this means that you can have (and often do have) multiple variables pointing to the same instance. For this reason, the internal object representation contains a refcount (Simply a count of the number of variables pointing to it). When an object's refcount drops to 0 (meaning that all the variables pointing to it either go out of scope, or are changed to somethign else) it is garbaged collected (as it is no longer accessable)...

您可以在文档中阅读有关 参考和 PHP 的更多信息...

You can read more on references and PHP in the docs...

免责声明:其中一些可能是对某些概念的过度简化或模糊.我的目的只是作为他们如何工作的指南,而不是内部发生的事情的确切细分......

Disclaimer: Some of this may be oversimplification or blurring of certain concepts. I intended this only to be a guide to how they work, and not an exact breakdown of what goes on internally...

哦,至于笨重",我认为不是.我认为它真的很有用.否则,您将到处传递变量引用.当应用程序一部分中的变量影响应用程序另一部分中的另一个变量时,这可能会产生一些非常有趣的错误.不是因为它通过了,而是因为在这条线上的某个地方做了一个参考.

Oh, and as for this being "clunky", I don't think it is. I think it is really useful. Otherwise you'd have variable references being passed around all over the place. And that can yield some really interesting bugs when a variable in one part of an application affects another variable in another part of the app. And not because it's passed, but because a reference was made somewhere along the line.

一般来说,我不怎么使用变量引用.我很少发现真正需要它们.但我确实一直使用对象引用.我经常使用它们,我很高兴它们是默认设置.否则我需要编写一些运算符(因为 & 表示变量引用,所以需要另一个来表示对象引用).考虑到我很少使用 clone,我想说 99.9% 的用例应该使用对象引用(所以让运算符用于较低频率的情况)......

In general, I don't use variable references that much. It's rare that I find an honest need for them. But I do use object references all the time. I use them so much, that I'm happy that they are the default. Otherwise I'd need to write some operator (since & denotes a variable reference, there'd need to be another to denote an object reference). And considering that I rarely use clone, I'd say that 99.9% of use cases should use object references (so make the operator be used for the lower frequency cases)...

JMHO

我还制作了一个视频来解释这些差异.在 YouTube 上查看.

I've also created a video explaining these differences. Check it out on YouTube.

相关文章