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

rus*_*nge 18 php clone reference

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

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

$b = 1;
$a =& $b;
$a = 3;
echo $b;
Run Code Online (Sandbox Code Playgroud)

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

$b = 1;
$a = $b;
$a = 3;
echo $b;
Run Code Online (Sandbox Code Playgroud)

应该输出1.

如果是这种情况,为什么需要克隆关键字?在我看来,如果我设置

$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
Run Code Online (Sandbox Code Playgroud)

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

irc*_*ell 44

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

对于除了对象之外的所

  1. 分配是按值(意味着如果您这样做,则会发生复制$a = $b.
  2. 可以通过执行来实现$a = &$b引用(注意引用运算符对变量进行操作,而不是赋值运算符,因为您可以在其他地方使用它)...
  3. 副本使用copy-on-write tehnique.因此,如果这样做$a = $b,则没有变量的内存副本.但是,如果你这样做$a = 5;,那么内存将被复制然后被覆盖.

对象:

  1. 分配是通过对象引用.它与正常变量的引用并不完全相同(我将在后面解释原因).
  2. 按值执行可以通过执行来实现$a = clone $b.
  3. 可以通过执行来实现$a = &$b,但请注意,这与对象无关.您将$a变量绑定到$b变量.它是否是一个对象并不重要.

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

$a = new stdclass();
$b = $a;
$a = 4;
Run Code Online (Sandbox Code Playgroud)

什么$b?嗯,这是stdclass......那是因为它不是写变量的引用,而是写对象......

$a = new stdclass();
$a->foo = 'bar';
$b = $a;
$b->foo = 'baz';
Run Code Online (Sandbox Code Playgroud)

什么$a->foo?是的baz.那是因为当你这样做时$b = $a,你告诉PHP使用相同的对象实例(因此对象引用).请注意,$a$b没有相同的变量,但它们均引用同一个对象.

考虑它的一种方法是考虑将对象存储为存储指向该对象的指针的所有变量.所以对象住在其他地方.当您将$a = $b在那里$b是一个对象,你正在做的是复制该指针.实际变量仍然是不相交的.但是当你这样做时$a = &$b,你会存储一个指向$b内部的指针$a.现在,当您操作$a它时,将指针链级联到基础对象.当你使用clone运算符时,你告诉PHP复制现有的对象,并创建一个具有相同状态的新对象......所以clone真的只是变量的副值副本...

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

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

免责声明: 其中一些可能过于简单化或模糊某些概念.我打算这只是一个指导它们如何工作的指南,而不是内部发生的事情的确切细分......

编辑:哦,至于这个"笨重",我认为不是.我认为这非常有用.否则你会在整个地方传递变量引用.当应用程序的一个部分中的变量影响应用程序另一部分中的另一个变量时,这会产生一些非常有趣的错误.并不是因为它已经通过了,而是因为引用是在某条线上进行的.

一般来说,我不太多使用变量引用.我很少找到他们的诚实需求.但我确实一直使用对象引用.我非常使用它们,我很高兴他们是默认的.否则我需要编写一些运算符(因为&表示变量引用,需要另一个表示对象引用).考虑到我很少使用clone,我会说99.9%的用例应该使用对象引用(所以让运算符用于较低频率的情况)......

JMHO

我还创建了一个解释这些差异的视频.在YouTube上查看.

  • **这个答案现在是钻石:))** (6认同)

sha*_*mar 5

简而言之:

在 PHP 5+ 中,对象是通过引用传递的。在 PHP 4 中,它们是按值传递的(这就是为什么它有运行时按引用传递,但已被弃用)。所以,你必须使用clonePHP5中的运算符来复制对象:

$objectB = clone $objectA;
Run Code Online (Sandbox Code Playgroud)

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

  • 不,对象不是通过引用传递的......我一直在阅读SO,这是错误的。PHP 4 和 5 之间的区别在于,在 PHP 5 中,对象变量实际上是对象引用。综上所述,在 PHP 4 中对象是按值传递的,在 PHP 5 中,对象引用是按值传递的。不,它几乎不一样。引用是一种复制抑制机制,对象引用是纯粹的旧间接。 (2认同)