【问题标题】:Why is there a difference between normal object instantiation and object instantiation by reference?为什么普通对象实例化和引用对象实例化之间存在差异?
【发布时间】:2013-01-24 20:06:49
【问题描述】:

我正在处理一个从 PHP 4 迁移到 PHP 5.2 的非常古老的项目,我们现在正在将该项目迁移到 PHP 5.4。我们提出了许多参考错误。这是一些困扰我的示例代码。

<?php

class book_shelf 
{
    protected $_elements = array();

    function addBook(&$element){
        $this->_elements[] =& $element;
    }

    function printBooks(){
        print(json_encode($this->_elements));
    }
}

class book 
{
    function __construct($title, $author="unknown") {
        $this->title = $title;
        $this->author = $author;
    }

    public $title = NULL;
    public $author = NULL;
}

function createBookShelf(){
    $bookshelf1 = new book_shelf();

    for ($i = 0; $i < 3; $i++){
        $book1 = new book("Book $i");
        $bookshelf1->addBook($book1);
    }

    $bookshelf1->printBooks();  
}   

createBookShelf();

?>

我希望这会创建 3 本书。但是元素数组中的每个对象最终都指向最后一个变量。另一方面,如果我通过引用创建一本新书,一切正常。 (例如$book1 =&amp; new book("Book $i");)下面是示例代码:

<?php

class book_shelf 
{
    protected $_elements = array();

    function addBook(&$element) {
        $this->_elements[] =& $element;
    }

    function printBooks(){
        print(json_encode($this->_elements));
    }
}

class book 
{
    function __construct($title, $author="unknown") {
        $this->title = $title;
        $this->author = $author;
    }

    public $title = NULL;
    public $author = NULL;
}

function createBookShelf() {
    $bookshelf1 = new book_shelf();

    for ($i = 0; $i < 3; $i++){
        $book1 =& new book("Book $i");
        $bookshelf1->addBook($book1);
    }

    $bookshelf1->printBooks();  
}   

createBookShelf();

我的印象是在 PHP 5.4 中通过引用创建对象是不必要的。知道为什么我会得到不同的结果吗?

【问题讨论】:

  • 删除 所有 对对象的引用。默认情况下,对象通过引用进行处理(分配、传递给函数/方法、返回)。
  • $this-&gt;_elements[] =&amp; $element; 你期望这个做什么?
  • $this-&gt;_elements[] =&amp; $element;这一行删除 & 解决了这个问题
  • 从 PHP 5 开始,所有对象都通过引用处理并不完全正确。看here

标签: php reference porting


【解决方案1】:

这是一个逻辑错误,与 PHP 版本无关。让我们看一下您的代码的以下 sn-ps:

//echo "phpinfo: " . phpinfo();
for ($i = 0; $i < 3; $i++){
    $book1 = new book("Book $i");
    $bookshelf1->addBook($book1);
}

...

function addBook(&$element){
    $this->_elements[] =& $element;
}

发生了什么?您通过引用传递 $book 。但是在每个循环中,您都会更改引用的值。在 for 循环结束时,$book 指向最后创建的 book 对象和引用。 (也有过这个错误;)

结论:您的 addBook 函数必须如下所示:

function addBook($element){
    $this->_elements[] = $element;
}

并保持添加书的代码不变 - 意思就像上面的例子一样,没有&amp;


*关于 =& 新 ... *

首先,如果你这样做,你会从 php 5.3 开始收到以下警告:

不推荐使用:不推荐使用通过引用分配 new 的返回值

因此你不应该将它用于新代码。

【讨论】:

  • 你能解释一下为什么 $book1 =& new book("Book $i");功能不同?文档似乎暗示“... =& new ...”现在的功能与“... = new ...”相同,但显然情况并非如此。我的假设是,即使 PHP 现在在实例化期间分配时默认使用引用,=& 提供的先前引用的“破坏”(如这里解释的 stackoverflow.com/a/3200017/459966)仍然有效。
  • 虽然我确信 Brad 很欣赏迄今为止每个人的 cmets,但我认为没有人真正回答过他的问题,即:“知道为什么我得到不同的结果吗?”看来他已经知道如何解决它,但想知道为什么他的两个示例的行为不同。
  • @Aaronius 我可以简化问题。你能解释一下吗:pastebin.com/Sq0HFxXv
【解决方案2】:

在您编写时,这是最初的 PHP 4 代码。这两个结构:

$book1 =& new book("Book $i");

function addBook(&$element){
    $this->_elements[] =& $element;
}

不再需要。第一个是“new by reference”,这从来都不是正确的做法,但有必要在 PHP 4 中使用对象而不是一直克隆对象

第二个是“按引用传递”,这可能是一个有效的概念,但就 PHP 5 中的对象而言,这不是必需的不应使用 对于作为对象的参数。

内存管理在 PHP 5 和 PHP 5.3 中得到了极大的改进。 PHP 5.4 通过发出警告帮助您迁移代码。尤其是“new by reference”很容易被发现和修复。

旧版(PHP 4):

$book1 =& new book("Book $i");

新的(当前 PHP 版本):

$book1 = new book("Book $i");

(不,它不会回来:))。对于通过引用传递的方法,我建议您在重构代码时删除引用并键入提示对象类型:

旧版(PHP 4):

function addBook(&$element){
    $this->_elements[] =& $element;
}

新的(当前 PHP 版本):

function addBook(Book $element){
    $this->_elements[] = $element;
}

那么你不仅修复了一个老问题,而且确保这个函数只使用预期的类型调用,你可以看到它总是用来获取一个对象(而不是一个仅在 PHP 4 中使用的变量引用用于对象,因为 PHP 4 受到限制。这不再需要,可以(并且应该扔掉这个旧东西!)被删除)。

将您的代码库置于版本控制之下,这样您就可以轻松地将更改应用到您的代码库,而不必担心破坏以前工作的任何内容。


我希望这会创建 3 本书。但是元素数组中的每个对象最终都指向最后一个变量。

这里的实际问题是您使用通过引用传递。当你分配

$book1 = new book("Book $i");

$book1 的引用计数为 1,并且未标记为引用。现在将它作为引用传递给 $bookshelf1-&gt;addBook($book1) 方法将把它变成一个引用计数为 2 的引用,因为在该方法中 reference 再次分配给类的私有成员:

for ($i = 0; $i < 3; $i++)
{
    $book1 = new book("Book $i");
    $bookshelf1->addBook($book1);
}

...

function addBook(&$element){
    $this->_elements[] =& $element;
}

然后您更改现在创建的引用。

所以现在当您将带有实例化的代码更改为:

$book1 = & new book("Book $i");

您总是 - 每个循环一次 - 只使用相同的变量名创建一个新的别名(引用)。因为您随后将此作为参考传递,所以它是您指定为参考的 新别名。因此,Book 中的数组指向三个不同的引用——而不是三次指向同一个引用。

我希望这能更好地解释问题。这也是我们说的原因:除非您知道自己在做什么,否则不要使用引用。 PHP 4 的情况是,许多用户习惯于在没有完全理解其工作原理的情况下使用引用(这是可以理解的,因为在 PHP 中这并不容易,我还需要稍微思考一下,直到我找到一个好的描述来分享这里 -希望这次能奏效:))。显然在这里使用 PHP 5 的好处是您不再需要对对象使用引用,这使得编写和阅读代码更加容易。


相关:

【讨论】:

    猜你喜欢
    • 2018-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多