コンストラクタとデストラクタ

デストラクタって・・・何???

コンストラクタ」は“オブジェクトの生成時に自動的にコールされる特殊なメンバ関数”でした。これはPHP4にもありましたが、PHP5になって「デストラクタ」という愉快な仲間が新登場しました。で、「デストラクタ」は“オブジェクトが消滅する時に自動的にコールされる特殊なメンバ関数”です。

PHP5式コンストラクタ/デストラクタ

PHP4では、クラス名と同じ名前のメンバ関数を定義すると、それが「コンストラクタ」になりました。PHP5でも、互換性のためにPHP4と同様にして「コンストラクタ」をつくれますが、“PHP5式”では「__construct()(アンダーライン2本で始まる)」が「コンストラクタ」となり、また「__destruct()(アンダーライン2本で始まる)」が「デストラクタ」になります。

ちなみに、“PHP4式”のコンストラクタと“PHP5式”のコンストラクタの両方が定義されていた場合は、“PHP5式”のコンストラクタがコールされます。

なお、PHPでは「__(アンダーライン2本)」で始まるクラスのメンバ関数名と通常の関数名を、“特殊な関数”として予約していて、「__(アンダーライン2本)」で始まる関数名を付けないよう推奨されているので、付けない様にしましょう。

デストラクタの呼ばれ時

“「コンストラクタ」の呼ばれ時”はご存知の通り、“「new演算子」でオブジェクトが生成された時”ですが、では“「デストラクタ」の呼ばれ時”の“オブジェクトが消滅する時”って具体的にどんな時なのか?それは“どの変数からも参照されなくなった時”です。以下のサンプルと結果をご覧下さい。

<pre>
<?php
    class BASE{
        protected function __construct(){
            echo "[][]   begin BASE constructor  [][]\n";
            echo "[][]   end   BASE constructor  [][]\n";
        }
        protected function __destruct(){
            echo "~~~~   begin BASE destructor  ~~~~\n";
            echo "~~~~   end   BASE destructor  ~~~~\n";
        }
    }
    
    class SUB extends BASE{
        public function __construct(){
            echo "[][][] begin SUB constructor [][][]\n";
            parent::__construct();
            echo "[][][] end   SUB constructor [][][]\n\n";
        }
        public function __destruct(){
            echo "~~~~~~ begin SUB destructor ~~~~~~\n";
            parent::__destruct();
            echo "~~~~~~ end   SUB destructor ~~~~~~\n\n";
        }
    }
    
    class HODE{
        private $id;
        public function __construct($id){
            $this->id = $id;
            echo "[][][] HODE({$this->id}) constructor [][][]\n\n";
        }
        public function __destruct(){
            echo "~~~~~~ HODE({$this->id}) destructor ~~~~~~\n\n";
        }
    }
    
    #オブジェクトを生成(変数に代入しない)
    echo '&gt;new SUB;', "\n\n";
    new SUB;
    
    $i = 0;
    
    #オブジェクトを生成(変数に代入する)
    echo '&gt;$hode = new HODE(0);', "\n\n";
    $hode = new HODE($i++);
    
    #オブジェクトを生成
    echo '&gt;$hode  = new HODE(1);', "\n",
         '&gt;$hode2 = new HODE(2);', "\n",
         '&gt;$hode3 = new HODE(3);', "\n",
         '&gt;$hode4 = new HODE(4);', "\n\n";
    $hode  = new HODE($i++);
    $hode2 = new HODE($i++);
    $hode3 = new HODE($i++);
    $hode4 = new HODE($i++);
    
    #アンセットする
    echo '&gt;unset($hode3);', "\n\n";
    unset($hode3);
    
    #「die()」でスクリプト終了。
    die("************ Called die() ************\n\n");
?>
</pre>
>new SUB;

[][][] begin SUB constructor [][][]
[][]   begin BASE constructor  [][]
[][]   end   BASE constructor  [][]
[][][] end   SUB constructor [][][]

~~~~~~ begin SUB destructor ~~~~~~
~~~~   begin BASE destructor  ~~~~
~~~~   end   BASE destructor  ~~~~
~~~~~~ end   SUB destructor ~~~~~~

>$hode = new HODE(0);

[][][] HODE(0) constructor [][][]

>$hode  = new HODE(1);
>$hode2 = new HODE(2);
>$hode3 = new HODE(3);
>$hode4 = new HODE(4);

[][][] HODE(1) constructor [][][]

~~~~~~ HODE(0) destructor ~~~~~~

[][][] HODE(2) constructor [][][]

[][][] HODE(3) constructor [][][]

[][][] HODE(4) constructor [][][]

>unset($hode3);

~~~~~~ HODE(3) destructor ~~~~~~

************ Called die() ************

~~~~~~ HODE(2) destructor ~~~~~~

~~~~~~ HODE(1) destructor ~~~~~~

~~~~~~ HODE(4) destructor ~~~~~~

「コンストラクタ」と「デストラクタ」の呼ばれ時を追跡してみますと、下記のようになります。

  1. new SUB; 」でオブジェクト生成。
  2. コンストラクタ」がコールされる。
  3. その後変数に代入される事なく文が終了。
  4. オブジェクトが何処からも参照されなくなったので「デストラクタ」がコールされる。
  5. $hode = new HODE(0);」でオブジェクト生成。
  6. 「HODE(0)」の「コンストラクタ」がコールされる。
  7. $hode = new HODE(1);」、「$hode2 = new HODE(2);」、「$hode3 = new HODE(3);」、「$hode4 = new HODE(4);」でオブジェクト生成。
  8. 「HODE(1)」の「コンストラクタ」がコールされる。
  9. $hode」が更新され、「HODE(0)」の「デストラクタ」がコールされる。
  10. 「HODE(2)」、「HODE(3)」、「HODE4」の「コンストラクタ」がコールされる。
  11. unset($hode3);」でアンセット。
  12. 「HODE(3)」の「デストラクタ」がコールされる。
  13. die()」でスクリプトを終了させる。
  14. 各変数がアンセットされていき、「HODE(2)」、「HODE(1)」、「HODE4」の「デストラクタ」がコールされる。
  15. スクリプト終了。

クラスを継承した場合、派生クラスのオブジェクトを生成・削除しても、基底クラスの「コンストラクタ」と「デストラクタ」は自動的にコールされないので、「parent::__construct()」や「parent::__destruct()」のように手動?でコールします。

更にもう1サンプルご覧下さい。

<pre>
<?php
    class HODE{
        private $id;
        public function __construct($id){
            $this->id = $id;
            echo "[][][] HODE({$this->id}) constructor [][][]\n\n";
        }
        public function __destruct(){
            echo "~~~~~~ HODE({$this->id}) destructor ~~~~~~\n\n";
            #未定義のメソッドをコールしてエラーを発生させる。
            $this->hode();
        }
    }
    
    $hode1 = new HODE(1);
    $hode2 = new HODE(2);
    $hode3 = new HODE(3);
    
    #未定義の関数をコールしてエラーを発生させる。
    hode();
?>
</pre>
[][][] HODE(1) constructor [][][]

[][][] HODE(2) constructor [][][]

[][][] HODE(3) constructor [][][]

Fatal error:その関数、未定義ですから。

~~~~~~ HODE(1) destructor ~~~~~~

Fatal error:そのメソッド、未定義ですから。

発生した時点でスクリプトが終了させられる「E_ERROR」エラーが発生しましたが、ちゃんと「デストラクタ」はコールされました。“「デストラクタ」は何が何でもコールされる”ようです。

が、よく見ると、「デストラクタ」を持つオブジェクトが3つあり、本来ならオブジェクトの消滅時にこの3つ分の「デストラクタ」がコールされるはずなのに1つ分しかコールされていません。

未定義の関数をコールして「E_ERROR」エラーを発生させ、「デストラクタ」がコールされましたが、さらにその「デストラクタ」内で未定義のメソッドをコールして「E_ERROR」エラーを発生させたら、それ以降、別のオブジェクトの「デストラクタ」はコールされませんでした。

die()」による人為的なスクリプトの終了の場合も「E_ERROR」エラー発生による強制終了の場合も、オブジェクトの消滅時には何が何でも「デストラクタ」はコールされますが、“「デストラクタ」内の処理によってスクリプトが終了する場合は、それ以降、別の「デストラクタ」はコールされません”

作成日:2004年12月21日 最終更新日:2004年12月21日
【通常モード で表示】