PHP循环类实例引用异常咨询:数组所有引用指向最后实例
嘿,这个问题我之前踩过坑,咱们一步步理清楚到底咋回事,以及比unset更靠谱的解决办法!
先搞懂核心问题:你大概率踩了「引用误用」的坑
结合你给的代码片段和描述,主要分两种常见场景:
场景1:你存的是「实例变量的引用」,不是「对象本身」
假设你的循环代码是这样的:
$foobars = []; for ($i = 0; $i < 3; $i++) { $foo = new FOOBAR($i); $foobars[] = &$foo; // 注意这里的&! }
PHP里&$foo表示你把变量$foo的引用塞进了数组,而不是对象的引用。每次循环时,$foo会被重新赋值成新的FOOBAR实例,数组里所有的引用都会跟着指向$foo当前的目标——也就是最后一次循环创建的那个实例。说白了,数组里的元素全绑定到了同一个$foo变量,不是各自独立的对象。
场景2:构造函数的引用参数,让所有实例的属性绑了同一个变量
看你写的FOOBAR构造函数:
public function __construct(&$input){ $this->val = $input; }
这里用了引用传递的&$input,如果循环里你复用了同一个$input变量,比如:
$foobars = []; $input = 0; for ($i = 0; $i < 3; $i++) { $input = $i; $foobars[] = new FOOBAR($input); }
每个实例的$val属性都会指向同一个$input变量的引用,不是当时$input的值。循环结束后$input是最后一次的2,所以所有实例的$val都显示2,看起来就像全是最后一个实例,但其实实例本身是不同的,只是属性绑了同一个变量而已。
为啥unset能“凑合用”?但为啥不推荐?
你说unset能解决,其实是因为循环里unset($foo)后,当前的$foo变量被销毁了,下一次循环会创建新的$foo,数组里的引用就不会被后续赋值影响。但这是个“治标不治本”的办法,而且如果操作不当,比如数组里的引用是实例的唯一引用,unset后实例可能会被PHP的垃圾回收机制提前回收,反而出问题。
正确的解决办法,从根源搞定
针对场景1:别存变量引用,直接存对象就行
PHP里对象本身就是以引用方式传递的,根本不需要加&。把代码改成这样:
$foobars = []; for ($i = 0; $i < 3; $i++) { $foo = new FOOBAR($i); $foobars[] = $foo; // 去掉&,直接存对象 }
这样数组里每个元素都是独立的对象引用,分别指向每次循环创建的不同实例。
针对场景2:要么改构造函数为值传递,要么每次用新变量
如果你不需要$val和外部变量保持联动,直接把构造函数的引用去掉:
public function __construct($input){ // 删掉& $this->val = $input; }
这样每个实例的$val都会存当时$input的副本,循环结束后每个实例的$val都是对应循环次的值,互不干扰。
如果确实需要$val和外部变量保持引用关联,那就在循环内部声明新的$input变量:
$foobars = []; for ($i = 0; $i < 3; $i++) { $input = $i; // 每次循环都创建新的$input $foobars[] = new FOOBAR($input); }
这样每个实例的$val引用的是不同的$input变量,就不会出现全指向最后一个值的情况了。
内容的提问来源于stack exchange,提问作者Trav Broad

