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

今回は実際にクラスを作ってみるね。
はーい!
じゃあ、まずクラスを作る時には必ず作るコンストラクタについて説明するね。
コンストラクタって?
コンストラクタっていうのは、クラスのオブジェクトが作られる時に、最初に実行される関数のこと。
new 演算子を使ってオブジェクトを作ると実行されるんだ。
じゃあコンストラクタって、呼び出さなくても実行されるの?
うん、そうだよ。
コンストラクタは初期化のための関数だから、わざわざ呼び出さなくても実行されるようになってるの。
初期化って、『var i = 0;』みたいに、変数を宣言する時に最初から値をセットしとくことだよね。
うん。整数型の変数とかだと、そうやって簡単に初期化できるからいいんだけど、 クラスの場合は1つのオブジェクトの中に変数がいっぱいあったりするから、コンストラクタ、つまり関数を使って初期化するんだ。
オブジェクトって変数をいっぱい持ってるの?
オブジェクトの中の変数は前回言った『属性』に相当するものだから、複数あるのが普通だね。
属性って、時計だと現在時刻とかアラームを鳴らす時刻とかなんだよね。
そ。そういうのはクラスの中の変数で表されるから、 いちいち宣言する時に全部初期化するのは大変でしょ。
だから、コンストラクタを使って、初期化が必要な変数を自動的に初期化できるようになってるの。
そっか、それでコンストラクタは自動的に呼び出されるんだね。
ん、そういうこと。
じゃあ、まずは簡単なクラスを作ってみるね。
うん。

<クラスの例>

// Test クラスの定義
class Test
{
    var a;  // メンバ変数

    // コンストラクタはクラス名と同じ名前の関数で、戻り値はありません
    function Test()
    {
        System.inform("Test クラスのオブジェクトが作られました。");
        a = 0;
        System.inform("メンバ変数 a が " + a + " に初期化されました。");
    }
}

// Test クラスのオブジェクトを作成します
var obj = new Test();

クラスも関数と同じように、まず定義が必要なんだ。
class Test』に続くブロックの中身が Test クラスの定義だよ。
クラスの定義の中に、変数の宣言と関数の定義が入ってるね。
うん。クラスの定義の中では、変数を宣言したり、関数を定義したりできるからね。
で、Test って名前の関数がコンストラクタ。
Test って、クラスの名前と同じだね。
そう。コンストラクタはクラス名と同じ名前の関数なんだ。
あ、そうなんだ。
クラスと同じ名前だから、これがコンストラクタってことがすぐに判るでしょ。
そだね。もしコンストラクタがどんな名前でもよかったら、どれがコンストラクタか判らなくなっちゃうもんね。
そう、だからクラスにつけられる名前も変数や関数につけられる名前と同じなんだ。
じゃあ、このスクリプトを実行してみて。
は〜い!

<最初に表示されたメッセージ>

「Test クラスのオブジェクトが作られました。」

<次に表示されたメッセージ>

「メンバ変数 a が 0 に初期化されました。」

このメッセージって、コンストラクタの中で表示してるの?
ん、そだよ。
まず、このスクリプトを実行すると、『var obj = new Test();』で Test クラスのオブジェクトが作られるから…
コンストラクタが呼び出されるんだよね。
そういうこと。じゃコンストラクタの中身を見てみて。

<コンストラクタ(抜粋)>

// コンストラクタはクラス名と同じ名前の関数で、戻り値はありません
function Test()
{
    System.inform("Test クラスのオブジェクトが作られました。");
    a = 0;
    System.inform("メンバ変数 a が " + a + " に初期化されました。");
}

1行目の inform メソッドが最初に実行されて、 「Test クラスのオブジェクトが作られました。」っていう最初のメッセージが表示されるんだね。
そうそう。じゃ2行目ね。
a っていう変数に 0 を代入してるけど…
これがさっき言ってたオブジェクトの中の変数なの?
そうだよ。
a っていう変数が Test クラスの定義の中で宣言されてるでしょ。
うん、最初の方で宣言されてるね。
こんなふうにクラスの中で宣言されてる変数をメンバ変数って言うから覚えといてね。
メンバ変数、ね。おっけー。
で、3行目で次のメッセージを表示して終わり。
コンストラクタは値を返さないから注意してね。
あ、そうなんだ。
でも引数は受け取れるんだ。こんなふうに。

<引数を持つコンストラクタの例>

// Test2 クラスの定義
class Test2
{
    var a;  // メンバ変数

    // コンストラクタの定義
    function Test2(x)
    {
        a = x;
        System.inform("メンバ変数 a が " + a + " に初期化されました。");
    }
}

// Test2 クラスのオブジェクトを作成します
var obj = new Test2(1);

ちょっと実行してみて。
はーい。

<表示されたメッセージ>

「メンバ変数 a が 1 に初期化されました。」

今度は「メンバ変数 a が 1 に初期化されました。」って表示されたね。
これって、コンストラクタの引数を 1 にしてるから?
そだよ。コンストラクタの1行目で引数の値を a に代入してるでしょ。
こんなふうにコンストラクタに引数をつけることで、オブジェクトを作る時にメンバ変数の初期値を指定できるんだ。
なるほどね〜。
じゃあ、次はデストラクタ
デストラクタって、コンストラクタと名前が似てるけど、何か関係あるの?
デストラクタは、コンストラクタとは逆に、オブジェクトが消滅する時に呼び出される関数なんだ。
えっ、オブジェクトって消えてなくなっちゃうことがあるの?
うん。必要とされなくなったオブジェクトは自動的に削除されてなくなっちゃうんだ。
必要とされなくなるってどういうこと?
じゃあ、デストラクタの説明も兼ねてオブジェクトが必要とされなくなるケースを見てみよっか。
例えばこんなスクリプト。

<オブジェクトが必要とされなくなる例&デストラクタの例>

// nothing クラスの定義
class nothing
{
    function nothing(){}  // コンストラクタ(何もしません)

    function finalize()   // デストラクタ
    {
        System.inform("オブジェクトが削除されました。");
    }
}

// nonsense 関数はクラス nothing のオブジェクトを作った後、何もせずにすぐ返ります
function nonsense()
{
    var obj = new nothing();
}

nonsense();

デストラクタは finalize っていう名前で、引数も戻り値もない関数なんだ。
あと、必要なければ省略しても OK。
このスクリプトだと、デストラクタの中で inform メソッドを呼び出して「オブジェクトが削除されました。」って表示するようになってるね。
つまり、nothing クラスのオブジェクトが消滅する時には「オブジェクトが削除されました。」って表示されるってこと。
で、このクラスは見ての通りコンストラクタとデストラクタがあるだけで、他には何もないクラス。
それって全然意味ないんじゃ…
うん、意味ないよ。
ま、説明用に作ったクラスだしね。
あ、そうなんだ…
で、nonsense 関数は、呼び出されると nothing クラスのオブジェクトを作るだけ。
これも意味ないねぇ…
まぁね。
この関数を呼び出した後、obj はどうなると思う?
えっ? …え〜っと、確か関数の中で宣言されてる変数って関数のブロックの中にあるから、関数の外からは使えないんだよね?
うん、そう。§1.12 で説明したよね。
ってことは、関数の実行が終わったら obj は使えなくなっちゃうのかな?
そ。その使えなくなったオブジェクトが必要とされなくなったオブジェクトなんだ。
あ、そういうことなんだ。
で、そういうオブジェクトは自動的に削除されるの。
じゃあ削除される時にデストラクタが実行されるってこと?
うん、そういうこと。
それを確認するために、このスクリプトを実行してみて。
うん。
……あ、ほんとだ。「オブジェクトが削除されました。」って表示されたね。
でしょ。
でも、厳密には削除される時じゃなくて、無効化される時にデストラクタが実行されるんだ。
無効化?
無効化っていうのは、もうこのオブジェクトは要らなくなったからいつでも削除しちゃっていいよ、 っていうマークをオブジェクトにつけることだよ。
要らなくなったオブジェクトは、まず無効化されて、その後に削除されるんだ。
ふぅん…そうなんだ。
で、普通はオブジェクトが要らなくなったら自動的に無効化されるんだけど、 手動でオブジェクトを無効化することもできるんだ。
そうなの?
うん。それが invalidate 演算子。

invalidate 演算子の使用例>

// Test3 クラスの定義
class Test3
{
    var a;  // メンバ変数

    function Test3(x)  // コンストラクタ
    {
        a = x;
    }
}

// Test3 クラスのオブジェクトを作成します
var obj = new Test3(1);

System.inform("メンバ変数 a の値は " + obj.a + " です。");

invalidate obj;  // オブジェクトを無効化します

// 無効化された後は obj は使えなくなるので、以下のスクリプトはエラーになります
System.inform("メンバ変数 a の値は " + obj.a + " です。");

えっと、inform メソッドの引数の中にある『obj.a』っていうのは…?
あ、これはクラスの外からメンバ変数の値を参照する時の書き方。
オブジェクト名.メンバ変数名』でクラスの外からメンバ変数の値を設定したり参照したりできるよ。
じゃあ、コンストラクタで a1 が代入されるから、 「メンバ変数 a の値は 1 です。」って表示されるってこと?
そういうこと。
で、その次でオブジェクトを無効化してるわけだけど、無効化するってことは「もうこのオブジェクトは要らないよ」って宣言することだから、 一旦無効化するとそのオブジェクトは二度と使えなくなるんだ。
ってことは、その次の行にある inform メソッドは…
もちろん実行できずにエラーになるよ。
試しに実行してみて。
うん。

例外メッセージ

あ、ほんとだ。「オブジェクトはすでに無効化されています」っていう例外が起きちゃったね。
そういうワケだから、無効化した後はそのオブジェクトを使わないように注意してね。
うん……でも、オブジェクトって要らなくなったら自動的に削除されるんでしょ?
だったらわざわざ手動で無効化する必要なんてないんじゃない?
ま、確かにそうなんだけどね。でも、要らなくなったオブジェクトがいつ削除されるのかは判らないから、 いつデストラクタが実行されるかも判らないんだ。
デストラクタの内容によっては思わぬ動作をする可能性があるから、 オブジェクトを使い終わったら invalidate 演算子で無効化することが推奨されてるの。
へぇ…そうなんだ。
ちなみに、オブジェクトが無効化されてるかどうかは isvalid 演算子を使うと判るよ。

isvalid 演算子の使用例>

// Test3 クラスの定義
class Test3
{
    var a;  // メンバ変数

    function Test3(x)  // コンストラクタ
    {
        a = x;
    }
}

// Test3 クラスのオブジェクトを作成します
var obj = new Test3(1);

if(obj isvalid)
    System.inform("オブジェクトは使用可能です。");
else
    System.inform("オブジェクトは既に無効化されています。");

invalidate obj;  // オブジェクトを無効化します

if(obj isvalid)
    System.inform("オブジェクトは使用可能です。");
else
    System.inform("オブジェクトは既に無効化されています。");

ちょっと実行してみて。
うん。
……最初は「オブジェクトは使用可能です。」って表示されて、 次は「オブジェクトは既に無効化されています。」って表示されたね。
isvalid 演算子は、オブジェクトが無効化されていれば偽、無効化されていなければ真になるんだ。
ってことは、最初の if の条件式は、まだオブジェクトが無効化されてないから真で、 次の if の条件式は、オブジェクトが無効化された後だから偽になるんだね。
うん、そういうこと。
じゃあ、コンストラクタとデストラクタについてはこんなとこだから、今回はここまでにするね。
ちょっとややこしかったけど、大体は解ったかな。
まぁ、コンストラクタとデストラクタの作り方は、これからクラスを作る時に改めて説明する機会もあると思うから、今はそんな感じで OK だと思うよ。
うん、りょーかい。
それじゃ、また次回ね。


前へ | TOP | 次へ