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

Posted

技术标签:

【中文标题】在 PHP 中,有人可以解释克隆与指针引用吗?【英文标题】:In PHP can someone explain cloning vs pointer reference? 【发布时间】:2011-04-06 10:25:36 【问题描述】:

首先,我了解编程和对象,但以下内容在 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 上的某些操作会影响 $obj_b,即使在没有引用运算符 ($obj_a = $obj_b) 的情况下进行分配也是如此。这给我今天带来了一个令人沮丧的问题,同时使用了我最终通过基本上修复的 DateTime 对象:

$obj_a = clone $obj_b

但我编写的大多数 php 代码似乎不需要像这种情况下那样显式克隆,没有它也可以正常工作。这里发生了什么?为什么 PHP 必须这么笨重??

【问题讨论】:

【参考方案1】:

简而言之:

在 PHP 5+ 中,对象是通过引用传递的。在 PHP 4 中,它们是按值传递的(这就是它在运行时通过引用传递的原因,这已被弃用)。因此,您必须在 PHP5 中使用 clone 运算符来复制对象:

$objectB = clone $objectA;

还要注意,它只是通过引用传递的对象,而不是其他变量。以下内容可能会让您更清楚:

PHP References PHP Object Cloning PHP Objects and References

【讨论】:

不,对象不是通过引用传递的......我一直在阅读 SO,这是错误的。 PHP 4 和 PHP 5 的区别在于 PHP 5 中的对象变量实际上是对象引用。综上所述,在 PHP 4 中对象是按值传递的,在 PHP 5 中,对象引用是按值传递的。不,它几乎不一样。引用是一种复制禁止机制,对象引用是纯粹的旧间接。 @Artefacto,我向您和您的知识鞠躬,先生 :)【参考方案2】:

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

除了对象之外的所有东西:

    按值分配(这意味着如果您执行$a = $b,则会发生副本。 可以通过$a = &$b 实现引用(注意引用运算符对变量进行操作,而不是赋值运算符,因为您可以在其他地方使用它)... 副本使用写时复制技术。因此,如果您执行$a = $b,则没有该变量的内存副本。但是,如果您随后执行$a = 5;,则内存将被复制并覆盖。

对于对象:

    通过对象引用分配。它与引用的普通变量并不完全相同(我稍后会解释原因)。 可以通过$a = clone $b实现按值复制。 引用可以通过$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 时,您将在$a 中存储一个指向$b 的指针。现在,当您操作$a 时,它会将指针链级联到基础对象。当您使用 clone 运算符时,您是在告诉 PHP 复制现有对象,并创建一个具有相同状态的新对象...所以 clone 实际上只是按值复制变量...

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

您可以在references and PHP in the docs...上阅读更多内容

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

编辑:哦,至于“笨拙”,我认为不是。我认为它真的很有用。否则,您将在各处传递变量引用。当应用程序的一个部分中的变量影响应用程序另一部分中的另一个变量时,这可能会产生一些非常有趣的错误。不是因为它通过了,而是因为沿线某处做了参考。

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

JMHO

我还制作了一个视频来解释这些差异。看看on YouTube。

【讨论】:

这个答案现在是钻石:) 我认为答案的结构可以更好,但 +1 表示实际上试图解释按引用传递和按值传递对象引用之间的区别。 琐事:在$obj = new InternalClass(); $obj = 4;中,第二个赋值实际上可以改变$obj所引用的对象。见wiki.php.net/internals/engine/objects#set 这是一个很好的对象引用示例,第 4 个示例:php.net/manual/en/language.oop5.basic.php @timaschew:对象是对象,其他一切都是原始的。数组不被视为对象...【参考方案3】:

我写了一个演示文稿来更好地解释 php 如何使用其变量管理内存:

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

看看 ;)

【讨论】:

以上是关于在 PHP 中,有人可以解释克隆与指针引用吗?的主要内容,如果未能解决你的问题,请参考以下文章

有人可以解释在 C++ 中通过指针传递和通过引用传递的目的是啥吗?

有人可以解释这个指针在这里指的是啥吗?

图像复制在 PHP 中重新采样,有人可以解释一下吗?

有人可以解释这个 PHP 代码吗? [关闭]

有人可以解释一下 C 中 signal() 语法的含义吗? [复制]

有人可以解释为啥以下代码中的总 = 7 吗?与静态整数总和? [关闭]