PHP マニュアル
前のページ | 第 14 章 クラスとオブジェクト | 次のページ

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

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

    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` の間の差異があるかどうか を確認してみましょう。

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

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

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

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

    /* output:
    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 */

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

    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
    */

前のページ | ホーム | 次のページ
特殊関数`__sleep` および `__wakeup` | 上に戻る | リファレンスの説明