Web aym.pekori.to

コンストラクタの内部での参照

コンストラクタの中で参照を作成すると結果が混乱する可能性があります。 本節ではチュートリアル形式で説明しますが、 この問題を避けるために役立つはずです。

<?php
class Foo
{
    function
Foo($name)
    {
        
// 内部への参照グローバル配列 $globalref を作成
        
global $globalref;
        
$globalref[] = &$this;
        
// name を指定した値に設定
        
$this->setName($name);
        
// それを出力
        
$this->echoName();
    }

    function
echoName()
    {
        echo
"<br>",$this->Name;
    }

    function
setName($name)
    {
        
$this->Name = $name;
    }
}
?>

コピー演算子 = により作成された $bar1 と 参照演算子 =& により作成された $bar2 の間の差異があるかどうかを 確認してみましょう。

<?php
$bar1
= new foo('set in constructor');
$bar1->echoName();
$globalref[0]->echoName();

/* 出力:
set in constructor
set in constructor
set in constructor */

$bar2 =& new foo('set in constructor');
$bar2->echoName();
$globalref[1]->echoName();

/* 出力:
set in constructor
set in constructor
set in constructor */
?>

明らかに違いはありませんが、実際には動作は非常に異なっています。つまり、 $bar1$globalref[0] は参照されておらず、同じ変数でもありません。 これは、"new" がデフォルトで参照を返さず、代わりにコピーを返すためです。

注意: (PHP 4 以降ではリファレンスカウンティングを使用しているため)、 参照ではなくコピーを返すことで性能が低下することはありません。逆に 多くの場合、参照を使うよりも単純にコピーを使った方が良い結果となります。 これは、参照の作成には時間がかかりますが、コピーの作成には 理想的には時間が全くかからないからです (ただし、大きな配列 またはオブジェクトでその一つが変更されると、次々に参照先の他の要素に 参照先に波及するといった場合を除きます)。

上記の記述が正しいことを示すために以下のコードを見てみましょう。

<?php
// ここで、name を変更してみます。どうなるでしょうか?
// $bar と $globalref[0] の両方共名前が変わると予想するかもしれません...
$bar1->setName('set from outside');

// 前記のようにこの場合は違います。
$bar1->echoName();
$globalref[0]->echoName();

/* 出力:
set from outside
set in constructor */

// $bar2 と $globalref[1] の差を見てみましょう
$bar2->setName('set from outside');

// うまく行けば、値が等しいだけでなく、同じ変数となります。
// つまり、$bar2->Name と $globalref[1]->Name も同じになります。
$bar2->echoName();
$globalref[1]->echoName();

/* 出力:
set from outside
set from outside */
?>

最後に別の例について考えてみてください。

<?php      
class A {
    function
A($i) {
        
$this->value = $i;
        
// ここで参照を使う必要がない理由を考えてみてください
        
$this->b = new B($this);
    }

    function
createRef() {
        
$this->c = new B($this);
    }

    function
echoValue() {
        echo
"<br>","class ",get_class($this),': ',$this->value;
    }
}

class
B {
    function
B(&$a) {
        
$this->a = &$a;
    }

    function
echoValue() {
        echo
"<br>","class ",get_class($this),': ',$this->a->value;
    }
}

// 以下の単純なコピーが、* 印を付けた行で望ましくない結果を生む理由を
// 考えてみてください。
$a =& new A(10);
$a->createRef();

$a->echoValue();
$a->b->echoValue();
$a->c->echoValue();

$a->value = 11;

$a->echoValue();
$a->b->echoValue(); // *
$a->c->echoValue();

?>

上の例の出力は以下となります。

class A: 10
class B: 10
class B: 10
class A: 11
class B: 11
class B: 11