これまで作ってきたプログラムは手続き型と呼ばれるものです。 手続き型の例として、雑誌の出版社と印刷所の関係を挙げられます。 出版社側は雑誌を印刷したいだけなのに、印刷の手順を知る必要があるし、印刷機のトラブルの対処まで行う必要があります。 これが手続き型プログラムの面倒なところです。
その面倒な手順を一挙に引き受けてくれるのがクラス(印刷所)なのです。 出版社側は印刷所が行う仕事と依頼の方法さえ知っていれば、ただ依頼するだけで雑誌は出来上がります。 たとえ印刷機が故障しようと、印刷所側が対処してくれるのだから。
クラスの役割がなんとなく分かってきたでしょうか?
クラスの基本
いよいよ、吉里吉里2/TJS2の真骨頂であるクラスについて説明する時が来ました。
クラスというのは、様々なデータを管理する事が出来るデータベースのようなものに似ています。 変数はもちろん、関数もクラスが管理することが出来るのです。

これまで、変数や関数には名前を付けてきました。
var name="山田";
function sum1(){ …… }
これは、クラスも例外ではなく、 TJS2で何かを指定する為には必ず名前を付ける必要があります。 早速、クラスの基本的な枠組みを作ってみましょう。
class クラス名
{
}
書式は関数と似ていますが、最後に()は書きません。 クラス名は変数や関数と同じく、変数の命名規則に則ります。 例えば、class1という名前のクラスを作るには以下のようにします。
class class1
{
}
このクラスは空っぽです。ですから何も管理していません。 次回以降、少しずつ管理するデータを追加していきます。
クラス定義の{}後にセミコロンは必要ありません。 ただ、書いてもエラーにはなりませんので、気持ち悪く感じる方は書いても良いでしょう。
クラスを使う理由
クラスという仕組みを説明する為に、ゲームのお話しをしましょう。 ここではRPGという種類のゲームを例に挙げています。
RPGを実際に作るとすれば、最初に何が必要でしょうか。 必要なものは山ほどありますが、何はともあれ主人公キャラクター、つまりプレイヤーを用意する必要があります。 プレイヤーというのは、敵を倒す役目を与えられた人物です。(厳密には全く違うのですが、細かい話はとりあえず横に置きましょう。)
今日、RPGプレイヤーには強さを指す数値、レベルがあります。 体力を指すヒットポイント(HP)や敵に与えられる攻撃力という数値もあります。 まずは、これらの変数を用意する必要がありそうです。
var playerName; //プレイヤーの名前 var playerLv; //レベル var playerHp; //ヒットポイント var playerAtk; //攻撃力
しかし、最近のRPGではプレイヤーは一人とは限りません。 仲間が一人、二人、ゲームによってはもっとたくさんいる場合もあります。 ここでは、プレイヤーを二人にしてみましょう。必要な変数は更に四つ増えます。
var playerName; //プレイヤー1の名前 var playerLv; //レベル var playerHp; //ヒットポイント var playerAtk; //攻撃力 var player2Name; //プレイヤー2の名前 var player2Lv; //レベル var player2Hp; //ヒットポイント var player2Atk; //攻撃力
これは、ちょっと変数の管理が面倒ですよね。 プレイヤー1のレベルが1上がる=playerLvに1を足す。 プレイヤー2のHPが20減る=player2Hpから20を引く。
この時、これら8つの変数は全て「プレイヤー」という仲間の変数です。 このように、ある共通した変数がある場合、クラスに管理させると便利です。
クラスはどのような変数も管理することが出来ますが、 全く共通点の無い変数をごちゃごちゃに管理させたのでは意味がありません。 だから、複数の共通した変数がある場合、これらをクラスに管理させる。これがクラスの使い道の一つです。
メンバー変数
では、先ほどのプレイヤーをクラスとして登録します。 名前は分かりやすくPlayerにしましょう。
class Player { var Name; var Lv; var Hp; var Atk; }
これで、クラスPlayerは「Name」「Lv」「Hp」「Atk」という四つの変数を管理するようになりました。 クラスは{}で囲まれています。つまりローカルスコープとなりますので、使う変数の名前は自由に付けることが出来ます。
この時、クラスが管理するデータをメンバーと呼びます。 上の例では、変数NameはクラスPlayerのメンバーであると言います。 また、メンバーが変数である時、この変数をメンバー変数と呼びます。変数Lvは、クラスPlayerのメンバー変数です。
さて、これでプレイヤーが作れました。実際にプレイヤーに名前を付けて見ましょう。
class Player { var Name; var Lv; var Hp; var Atk; } Name = "プレイヤー1";
エラーが出てしまいました。どうやらこの代入は出来ないようです。 これまでの知識から理由を推測すると、ブロック{}内で指定されたローカル変数は外部から参照出来ないからだと思われます。
ですが、クラスにおいては根本的に違います。 今の段階ではプレイヤーという枠組みを作っただけで、まだ実物が存在していないからです。 …実物、つまりプレイヤーという人物はまだ完成していないという事です。
オブジェクト
クラスは関数と同じく、ただ書いただけでは何も実行できません。しかし関数とは少し違います。 クラスというのは、簡単に言えば設計図のようなものに過ぎないからです。 関数は存在しているものに対し、クラスという実体は存在していないのです。(難しい表現ですね。)
家やバイク、船などにも設計図がありますが、この設計図があっても、何も出来ませんよね。 家の設計図だけがあっても扉を開ける事すら出来ません。そもそも家自体が無いのだから。 つまり、この設計図を元に、実物を作る必要があるのです。
でもTJSでは、実際に実物を作るのに職人さんは必要ありません。 ただ一行、次のように書けばよいのです。
new クラス名();
単語newは、右側に書いたクラス名の実物を作ります。 そして、作った実物に名前が無いと参照出来ませんので、変数を用意して実物を入れます
var 変数名 = new クラス名();
この時、クラス(設計図)を元に作られた実物をオブジェクトと呼びます。(インスタンスとも呼びます) では、Playerクラスの実物を作ってみましょう。
class Player { var Name; var Lv; var Hp; var Atk; } var player1 = new Player();
これでplayer1という変数にクラスPlayerの実物が作られました。 この時、player1はクラスPlayerのオブジェクトであると言い、 Playerクラスの実体ですから、メンバー変数「Name,Lv,Hp,Atk」の四つを実際に持っています。
設計図を元に、プレイヤーの数を三人にしてみましょう。
var player1 = new Player(); var player2 = new Player(); var player3 = new Player();
player1~player3というプレイヤーが作られ、 それぞれが固有の変数「Name,Lv,Hp,Atk」を保持する事になります。
C++クラスとの違い
TJSでは、new演算子を使ってオブジェクトを作る時、 例えコンストラクタが引数を取らなくても()は省略できません。必ず次のように書く必要があります。
new class();
そしてもう一点。TJSのnew演算子は、C++とは大きく異なります。例えば
p = new myclass();
この時、pはmyclassのオブジェクトへのポインタと、そのコンテキストへのポインタを示します。 ただ、TJSではポインタが扱えませんし、データ型を自動で処理しますので、 例えば次のような代入を行うと、p2はp1と同等のポインタを持つようになります。
p2 = p1
ややこしいですが、このp2とp1は完全同一と考えてよく、p1が派生オブジェクトだった場合、 p2も派生オブジェクトそのものとして機能します。
(p2はポインタを指しますが、オブジェクトです。メンバアクセスにはピリオドを使って obj.member と書きます。)
メンバーの参照
さて、プレイヤーという実物が作れました。 このプレイヤーには名前がありませんから名前を付けたい所です。 名前を指すメンバー変数はNameです。
この名前は設計図の段階で決めてしまうのは非常にまずいですね。 船の設計図の段階で、船の名前は「プログラム丸だ!」なんて決められたら、誰もそんな船を使いたくない。 だから、名前は後から付けられるようにしなくてはなりません。 つまり、メンバー変数を外部から参照しなければならない。
単純に「Name="名前"」という指定ではグローバル変数になりますし、 どのプレイヤーの名前なのかも分かりません。 よって、参照したいオブジェクトを指定するために、次のように書きます。
オブジェクト名.メンバー名
オブジェクト名に続いてドット( . )を書き込み、参照したいメンバーの名前を書きます。 例えば、Playerクラスの実物であるオブジェクトplayer1を参照します。 このメンバー変数であるNameを参照するには次のように書きます。
player1.Name
では、Playerクラスの実物を作り、 メンバー変数のNameに値を代入するまでの工程を見てみましょう。
class Player { var Name; var Lv; var Hp; var Atk; } var player1 = new Player(); var player2 = new Player(); var player3 = new Player(); //オブジェクトplayer1の名前(変数Name)を変更 player1.Name = "主人公"; System.inform( player1.Name );
C++
クラスのメンバーは、このように何処からでも参照出来ます。 つまり、TJSにおけるクラスのメンバーは、 全てpublicメンバーである、と言えます。
無事、オブジェクトplayer1のメンバー変数Nameに値を代入できました。 同様にplayer2、player3の名前を変えることも出来ますね。
しかし、実はまだ問題があります。 設計図の段階でNameという変数名を変えたくなったらどうしましょう。 例えば「userName」という変数名にしたかったら。 そのまま変えてしまうと、外部でその変数を参照できなくなります。 つまり、参照する側の名前も同じように変える手間が生じます。
class Player { var Name1; //ここを変えると } var player1 = new Player(); player1.Name1 = "主人公"; //ここも変える必要がある
クラスにしたという事は、そのメンバー変数は多くの場所で使われる事が多いはずです。 そうなると、全ての場所の変数の名前も同様に変えなければなりません。 これは手間であると同時に、タイプミスもありえます。
という事で、クラスのメンバー変数自体は、出来るだけ外部から直接参照しない方が良いのです。 メンバー変数名は、出来るだけそのクラスの中だけで使う。 これは関数でも使ったローカル変数と全く同じ考え方なんですね。 クラスや関数限定で使う変数には独立性を持たせなければならない。
では、実際にどうすれば良いのか?答えは簡単で、関数を使えばよいのです。 最初に言ったとおり、クラスは関数もメンバーにする事が出来ます。 メンバー変数は、この関数を通じて操作し、外部からはこの関数を呼び出すようにする。 これで、外部にとっては、メンバー変数にどのような名前が使われているのか?を気にする必要が無くなります。
メンバー関数
関数をクラスに管理させるには、変数と同じようにクラスの内部に書けば良いのです。 つまり{}の中に書けばよいのです。次のように。
class class1 { function func1() { //処理 } }
このように、クラスが管理している関数の事をメンバー関数と呼びます。またはメソッドとも呼ばれます。 当入門では、変数と呼び方を合わせるために、メンバー関数と呼ぶ事にします。 (覚えやすいように、ご自分で呼び方を変えて構いません)
C++
メンバー関数の本体は、基本的に全てクラス内部に書き込みます。 つまり、インラインメンバー関数のような扱いを受けます。
外部の関数をメンバーとする事も可能なのですが、 基本的には、全てインライン関数を使うものだ、としておいてください。 外部の関数を(擬似的に)メンバーとする方法は、少し後で説明します。
メンバー関数も、普通の関数と何も変わりませんから、処理を自由に書く事が出来ます。 しかし、そのクラスと全く関係の無い処理を書いても意味はありません。 ですからメンバー関数は、そのクラスに関連した処理だけを行わせるのが普通です。
今行いたい事は、プレイヤーに名前を付ける事です。 ですから、メンバー変数Nameに値を入れる関数を作ります。 また、Nameに値を入れても、その内容を参照出来ないと意味が無い。 そこで、変数Nameの値を返す関数も作ります。その例が以下になります。
class Player { var Name; //メンバー変数Nameに名前をセットする function setName( name ) { Name = name; } //メンバー変数Nameの値を返す function showName() { return Name; } } var player1 = new Player(); player1.setName("主人公"); System.inform( player1.showName() );
まず、メンバー関数setNameが、メンバー変数Nameに値を代入する為の関数です。 引数としてnameを受け取りますので、次のように呼び出すことでNameに値を代入できます。
オブジェクト名.setName( 名前 );
setNameは関数ですから、最後に()を書く必要があります。 これがメンバー変数の参照とは違うところなので注意してください。 このメンバー変数Nameを参照する為に、メンバー関数showNameを呼び出すのですが、この関数はNameを返すだけです。 これを変数などに代入すると、値を直接受け取る事も出来ます。
var name = オブジェクト名.showName()
これで、変数nameに、メンバー変数Nameの値が代入できました。
もし、メンバー変数Nameの名前を途中から変えたくなっても、自由に変える事が出来ます。 修正する部分は、その変数を操作しているメンバー関数の部分だけで良いからです。
class Player { var firstName; //ここを変えると function setName( name ) { firstName = name; //修正はここだけ } } var player1 = new Player(); player1.setName("主人公"); //呼び出す側は変更なし!
これで、名前をつける時は関数setNameを呼び出せば良い事になりますから、 クラスを使う側にとっては、実際にどのような形でプレイヤーの名前を保存しているのか、 その仕組みを知る必要がなくなるのです。
System.informの正体
ここまで来るのに長かったですが、これまで読んでいれば、System.informが一体何だったのか、 お分かり頂けるのではないでしょうか。
結論を言えば、Systemとは吉里吉里自身が持っているクラスのオブジェクトだったんです。 これまで文字を表示するのに、次のように使っていました。
System.inform( 文字 );
この書き方、もう分かりますよね。 Systemというオブジェクトのメンバー関数であるinformの呼び出しを行っている。 すなわち、値を受け取りそれを表示するという処理を行う、Systemクラスのメンバー関数。 これがSystem.informの正体です。
より詳しくSystemクラスを知るには、吉里吉里2リファレンスを参照してください。
メンバー関数の名前や仕様は変えない事
そもそも、メンバー関数を用意する一番の目的は、メンバー変数を保護する事です。 メンバー変数の名前を変えようが、どのように使おうが、 クラスを使う側(つまり外部から)は、その変数の仕組みを知る必要が無いようにしたものです。
それらを操作するメンバー関数は、外部から使われる為に用意したメンバーです。 という事は、当然この関数の名前を変えると、 呼び出す側の名前も変えないといけなくなる。これでは本末転倒というもの。
よってメンバー関数の名前は最初に決めたらもう変えない。 そして、引数の数や処理の内容なども、本当に必要なものをよく考え、 一度作ったらもう変えないという事が大切です。
例えば、関数の呼び出し側はメンバー変数Nameに値を代入したつもりなのに、 その関数の処理自体が変わってしまっては大問題です。
つまりメンバー関数を作る時は後々の事をよく考えて作らないと、 思わぬしっぺ返しを受ける事になるかもしれません、という事です。
参照渡し
値の代入において、それがオブジェクトかそうではないかによって、代入時の動作が異なります。 今回はクラスのオブジェクトを別の変数に代入した場合の動作を見ていきます。
実体渡し
var x = 10; var y = x; System.inform(y); y = 20; System.inform(x);
これまで使ってきた、変数に値を入れたり、変数に変数の値を入れたりする処理です。 数値の10を変数xに代入し、変数xの値を変数yに代入しています。 ですから、変数yの値を変更しても変数xの値に変化が無い事を確認してください。 すなわち、オブジェクト以外の代入の場合はその値の実体をコピーするのです。

参照渡し
class Calc { var num; function set( Num ) { num = Num; } function show() { System.inform(num); } } var calc1 = new Calc(); calc1.set(5); calc1.show(); // オブジェクトcalc1をcacl2に代入 var calc2 = calc1; calc2.set(200); calc1.show();
var calc2 = calc1;
この代入は、オブジェクトclac1を実際にコピーしているわけではありません。何故なら…
calc2.set(200);
のように、calc2のメンバー関数setを呼び出し、numに代入しているにも関わらず…
calc1.show();
で確認すると、オブジェクトcalc1のメンバー変数numが200に変化している事が証明です。 すなわち、オブジェクトの代入は実体を代入しているのではなく、 オブジェクトを指し示す参照を代入している、という事です。

言い換えるなら、オブジェクトを代入する事は出来ず、 オブジェクトを別名で利用できる、というだけの事なのです。
もう少し簡単に言うなら、オブジェクトを代入した変数calc2は、 calc1を操作する為のリモコンのような役割をします。
var calc1 = new myClass(); var calc2 = calc1; calc2.num = 1 ……calc1のメンバー変数numを操作 calc2.set() ……calc1のメンバー関数setを呼び出す
関数に渡す引数にも同じ事が言えます。
var x = new myClass(); func1(x); function func1( obj ) { }
この時のオブジェクトxと引数objは相互に影響しあっているため、 objでメンバー関数を操作するという事は、オブジェクトxを操作する事に等しくなります。
クロージャ
ある変数にオブジェクトを代入すると、そのオブジェクトの別名として機能するという事が分かりました。 この機能をうまく使うと、あるオブジェクトのメンバー関数を外部から使う事が出来るようになります。
class A { var num; function set(Num) { num = Num; } function show() { return num; } } var obj = new A();
これでクラスAのオブジェクトobjが作られました。 関数setはメンバー変数numに値を代入する処理、showは変数numの値を返す処理をします。 ここで、この関数を外部から使ってみる…つまり、関数の代入を行うとどうなるのでしょうか。
obj.set(100); //変数numに100をセット System.inform( obj.show() ); //100と表示 var set2 = obj.set; //objのメンバー関数を代入 set2(200); //一見すると、グローバルな関数の呼び出し? System.inform( obj.show() ); //200と表示
結果を見れば分かるとおり、show2は単に関数obj.setを代入したわけではなく、 それがどのクラスとオブジェクトに属しているかの情報を持っているのです。 ですから set2() と呼び出す事で、オブジェクトobjの関数を呼び出すという動作をしてくれるのです。 この機能はクロージャと呼ばれます。
クロージャを使うと、例えば最初に出てきたプレイヤークラスにも応用できますね。 プレイヤー1がプレイヤー2の名前を参照、プレイヤー2がプレイヤー3の体力を回復……。
難しい話ですので、なんとなく理解できれば大丈夫です。 とりあえず、次の点を覚えておいてください。後に複雑なプログラムを作るときに役に立つはずです。
メンバー関数は、クラスだけではなく、どのオブジェクトのメンバーなのか、という情報を持っている