データのタイプいろいろ

 今回は「変数」を超えたデータ、そして「オブジェクト指向」について見ていきましょう。

変数の限界
 値を入れるもの、それが変数です。でも、プログラムを組んでいくと、変数だと分かりづらくなっていきます。
 例えば、「下駄箱」というデータを扱いたい場合、どうすればいいでしょう。左上角を起点にしてGeta01_01, Geta01_02...みたいな感じで変数を作っていったら、とんでもなく大変なことになります。
 同じように「自分自身」というデータを扱いたい場合も大変です。身長、体重といった値、名前やニックネームといった値、様々なデータが複合的に存在します。これらを別々に扱うのも大変でしょう。

 データは無味簡素な数字です。コンピューターはそれを操作します。
 でも、人間はそういう無味簡素なものを扱うのを苦手とします。データに意味を持たせたい。そして、プログラムの中に組み込みたい。つまり、プログラムの中で分かりやすい形でデータを管理したい、それがオブジェクト指向の始まりです。
 では、そのための簡単な、データのタイプを紹介しましょう。

配列
 最初の下駄箱の例を解決するのが配列です。配列とは、変数を10個とか20個とかまとめて作るもので、例えば

/*	C言語系の場合。	*/
	int iTest[256];	/*256個の変数を作製します。*/

	iTest[0] = 128;
	iTest[1] = 256;
	iTest[2] = 512;
	

 といった感じに使うことができます。「どの変数に入れるのか」というのは[](大かっこ)の中の数字で選択します。ここにはもちろん変数も使えるので、forループなどと組み合わせることでデータの扱いを簡略化できます。
 C言語系とJAVA系は、上の例のような形で配列を作製することができます。Visual Basic系とPascal系は次のようになります。

'	VB系の場合。
	Dim iTest(256) As Integer	'256個の変数を作製します。

	iTest(0) = 128
	iTest(1) = 256
	iTest(2) = 512
	
{	Pascal系の場合。	}
	iTest: array [1..256] of Integer;
begin
	iTest[0] := 128;
	iTest[1] := 256;
	iTest[2] := 512;
	

 このように、まったく違います。大変ですねぇ。実際、ここから先は言語によって大きく違います。そして言語によってはサポートしていないものもあるので注意してください。
 でも、文法は違ってもどの言語も意味は同じです。その辺は安心してください。

構造体
 さて、今度はもうひとつの方、「自分自身」を表すデータです。こういった「変数がいっぱい詰まったデータ」を構造体と呼びます。英語で書くとstructure。会社なんかの再構築はRestructuring、いわゆる「リストラ」です。こういう風に憶えると分かりやすいですね。
 構造体は、変数の中に変数を持っているような形です。「自分自身」のデータの中に、「身長」というデータ、「体重」というデータ、「名前」というデータが入っている、そんな感じです。それらの変数ひとつひとつで、値を変更したり取得したりすることができます。
 ここからは、ほんっとーに言語ごとにバラバラになっちゃってかなり違うので、それぞれの例は紹介せず、簡単な説明だけ行います。

 C言語系はstructという予約語を使用します。JAVA系は構造体を持たず、代わりに後で紹介する「クラス」と呼ばれるものを使用します。
 Visual Basic系での構造体は「ユーザー定義型」と呼ばれ、Typeという予約語を使用します。
 Pascal系での構造体は「レコード型」と呼ばれ、recordという予約語を使用します。
 この辺はぜーんぜん違うのに、使う時は一緒。例えば「人間構造体」にWeightという変数があり、その「人間構造体」として宣言された変数Wateがあったとしたら、

	Wate.Weight = 67;
	

 というふうに、ピリオドを挟むことで構造体の中の変数に値を入れたり取得したりすることができます。ピリオドを「の」に置き換えて読むと分かりやすいかもしれません(つまり「WateのWeight」と読む)。この形式は基本的にどの言語でも同じなので(C言語系のポインタは別)憶えるのは簡単でしょう。もっとも、そのおかげで他の部分で混乱を招くのですが……。

オブジェクト指向
 「人間構造体」ができた、じゃぁそれを扱おうということで、「体重適性度関数」なるものを作るとします。この関数は、身長、体重、年齢からその人が肥満かやせ過ぎかをチェックするものだとしましょう。
 この関数の設計、つまり「どんな引数を取ってどんな戻り値にするか」というイメージ、どのようなものが浮かびますか? そのまま構造体を渡すという人もいれば、みっつのデータだけ渡すという人もいるでしょう。
 こういった場合には、基本的に構造体をそのまま渡す方がいいことになっています。構造体の各データを渡す場合、そのデータを間違えて入れてしまったり、さらにそれ以外のデータを入れてしまうかもしれません。また言い換えれば、「それができる」ということでもあります。データの管理がまた難しくなります。

 構造体を渡す場合、その関数は「その構造体専用の関数」ということになり、基本的に他の構造体とは関係がなくなります。この考え方を突き詰めると、「専用の関数も構造体の属性のひとつ」という風に捉えることができます。
 ここからもう一歩進んでみます。構造体専用の関数がある場合、その関数を中心に構造体にアクセスできるとしたらどうでしょう。何度も言っているように、関数を使用することでデータをパッケージ化できます。ということはつまり、構造体の中のデータもパッケージ化できるということです。

 例えば、ビデオテープ。ビデオテープはビデオデッキを使うことで、中身を見ることができます。つまり「ビデオテープ」という構造体の中のデータを「ビデオデッキ」という専用の関数でのみ取り出せる、ということです。もちろん、ビデオデッキ以外の何らかの方法でも、テープの中のデータを取り出すことができます。が、それはイレギュラーな方法です。
 ここに、ふたつの立場が存在します。ひとつはテープ側。もうひとつはデータ取得側。これを読み替えて、テープを「オブジェクト」、取得側を「プログラマー」としましょう。

 これまでは(特にC言語では)プログラマー重視の風潮がありました。データとはプログラマーが管理するもの、という観点からです。コンピューターは人間が作りだしたもので、人間が管理するものなので、それも当然です。
 ですが、コンピューターが自動的に管理しても、その管理する部分を人間が作るのだから、それも人間の管理と同じ事です。つまり「コンパイラに管理させる」という方面へと少しずつ傾いてきます。
 コンピューターの性能が上がると、大きく複雑なデータを扱うことができるようになってきました。昔のメモリはキロバイト単位でした。それが今ではメガ、さらにギガ単位にまで増えています。そこで、さらに綿密な管理が自動的に行えることが望まれました。
 そこで、データはデータそのものに管理させよう、そういう考え方が現れました。つまり、データを「オブジェクト」、何か意味のある「物」へと昇華させ、関数を含めて色々と属性を与えることで、オブジェクトが自らを管理する、という方向へと進んでいきました。
 そして今あるのが「オブジェクト指向(Object Oriented)」というものです。「Oriented」とは「その方向に向いた」という意味です。まさに、プログラマーからオブジェクトへと風向きが変わったのです

「オブジェクト指向」の実際
 長々と書いてきましたが、実際「オブジェクト指向プログラミング」とはどのようなものか説明してませんでしたね。
 簡単に言えば「オブジェクト中心」ということです。先ほどの例で言えば「ビデオテープはビデオデッキでなければ見られなくする」ことです。ビデオテープはビデオデッキ以外の方法を用いてアクセスすることができます。が、それはビデオテープにしてみれば「不正なアクセス方法」です。ビデオテープは、ビデオデッキを使って、データを取得して欲しいのです。
 こういった「データ側でデータの取得方法を設定することができる」ことが、プログラミング言語としての「オブジェクト指向」です。関数以外にも、様々な「取得方法」を設定することで、オブジェクト自身に「オブジェクトの振る舞い方」を決めさせることができるようになります。

 では、現在プログラミングにおいてどのように「オブジェクト指向」は使われているのでしょうか。実際には「色々なところで様々な形で使われている」と言えますが、そのため、ここではひとつひとつ紹介することができません。
 一応「コンポーネント」「クラス」といった単語が出てきたら、「オブジェクト指向」を思い出してください。
 細かい説明はしない代わりに、「オブジェクト指向」の主な特徴を上げておきます。ただし、これらは言語、アーキテクチャー、バージョンに大きく依存するため、どの機能が使えてどの機能が使えないのか、自分が使う場合にはちゃんとチェックしておいてください。

変数+関数
 オブジェクト指向のもっとも基本的な部分です。構造体のような形で変数を持ち、それらを操作するための専用の関数を持ちます。これらを「メンバ変数」「メンバ関数」と呼びます。
 オブジェクトの中には、「構造体」的な部分があまりなく、実際には関数のみが後悔されている物もあります(後述の「アクセス指定」を参照)。これは見かけ的には「オブジェクト」そのものといった感じでしょう。

初期化(コンストラクタ)と後処理(デストラクタ)
 変数と違って、大きく複雑なデータは作製するのも大変です。
 例えば「画像イメージ」を保存するデータを作製する場合、まず作るときに「イメージのサイズはどれにするか」「色数はどうするか」「白で埋めるか黒で埋めるか透明で埋めるか」といった各パラメータを最初に設定する必要があります
 また、大きなイメージは普通に変数を作製せず、メモリから直接必要な分だけ取得するのが普通です。その取得、そして、要らなくなったときの解放が必要になってくるわけです。

 これらをこなしてくれるのが「コンストラクタ」「デストラクタ」です。
 コンストラクタは「オブジェクトが作られた時に呼び出されるメンバ関数」で、この関数の中で必要な下準備を行うことができます。逆にデストラクタは「オブジェクトが破棄される時に呼び出されるメンバ関数」で、要らなくなったメモリを削除したりします。
 これらの機能を使うことで、オブジェクトの前処理にも後処理にも気を使う必要がなくなります。

演算子のオーバーロード
 オブジェクトからオブジェクトへと代入する場合、すべてのメンバがそのままコピーされます。が、そうではなく、渡されるデータをチェックしたり加工したりしてから格納したい場合もあるでしょう。また、オブジェクトから他の種類のオブジェクトへと代入を行いたい場合、各メンバを渡すのではなくそのまま=を使って代入した方が分かりやすい場合もあるでしょう。

 これらをクリアするのが演算子のオーバーロードです。特定の演算子とデータの組み合わせに対して新しい定義を作製し、新たに作製した関数を呼び出すことができます。
 例えば「あるオブジェクトとオブジェクト=で代入されようとしたとき」に対してのオーバーロード関数を作製することで、実際に行おうとしたときにその関数が呼び出される仕組みになります。多くの場合、その関数はオブジェクトのメンバ関数として作製されます。そのため、呼び出されるときには片方のオブジェクトのメンバ関数として呼ばれ、もう片方のオブジェクトが引数として渡される形になります。

 これは=だけでなく、+にも<にも−−にも、たいがいの演算子で行うことができます。これらをうまく使うことで、さらに「オブジェクトらしく」することができます。ただし、便利だからといってむやみに使うと、直感的に分かりにくくなってしまう可能性があります。そうなると「オブジェクトらしく」ないので注意しましょう。

アクセス指定
 オブジェクト内のデータをどのようにするのかまだ決まっていないうちにプログラムに組み込む必要がある場合もあるでしょう。たとえ決定していてもあとで変更する可能性が高い場合もあります。
 このような場合には、メンバ関数のみで内部データの設定と取得を行うのが良策です。関数の使用による「パッケージ化」を用いれば、データが変わってもアクセスするためのメンバ関数の中身を変更するだけで、関数の呼び出し側を変更する必要はなくなります。
 ところが、オブジェクトが「構造体+関数」だった場合、めんどくさくなってその関数を用いずに直接データへとアクセスする可能性が出てきます。自分以外のプログラマーがそのオブジェクトを使用する場合にはその可能性が特に高くなるでしょう。それを防ぐのが「アクセス指定」です。

 アクセス指定には主に「パブリック」「プライベート」があります。
 「パブリック」は構造体のように外からデータへとアクセスできます。が、「プライベート」にすればそれができなくなり、メンバ関数からのみアクセスが可能になります。こうすることで、オブジェクトの管理が楽になります。

 さらに、このアクセス指定はメンバ関数にも行うことができます。「パブリック」に指定すれば、構造体のようにオブジェクトを宣言した関数から呼び出すことができます。「プライベート」に指定すればオブジェクトのメンバ関数からしか呼べなくなります
 この機能は、内部データを変更する関数をむやみに呼び出してもらいたくない場合に使います。実際、オブジェクトのメンバ関数は「外部とアクセスするための窓」という意味が強くなります。そういった新たな意味の関数と、これまでの「処理を行う単位」としての関数を共存させるため、前者をパブリック、後者をプライベートに指定することが必要になるでしょう。

継承(インヘリタンス)
 「猫」というオブジェクトがあった場合、「三毛猫」というオブジェクトを新たに作製したい場合にはどうすればいいのでしょう。「三毛猫」は「猫」オブジェクトが持つ属性をすべて持っていて、そこに「毛色タイプ」というデータが加えられているだけの違いということにしましょう。そうなると、「三毛猫」オブジェクトをさらに作製するのはめんどくさく、大変です。「猫」のコピーを作製して「三毛猫」を作製したとしたら、「猫」に修正を加える度に「三毛猫」にも修正を加えなければならなくなります。

 ここで出てくるのが「継承」という概念です。「継承」とは「元からあるオブジェクトに新たに属性を追加する」ことです。ここでは「猫」オブジェクトから継承して「三毛猫」オブジェクトを作ることになります。
 継承にもアクセス指定を行うことができます。パブリックなら、継承元(「猫」)のパブリックなメンバは、派生先(「三毛猫」)でもパブリックなメンバとして扱われます。逆にプライベートなら継承先のメンバ関数からのみアクセスが可能になります。ただし、基本的にプライベートな継承は使用しません。プライベートな継承を行うことで、継承元の属性のほとんどが死んでしまうからです。基本的にこれは、継承元のオブジェクトを派生先のオブジェクトのメンバ変数として持つのと同じことでしょう。
 また、たとえパブリックな派生を行ったとしても、継承元のプライベートなメンバにはアクセスできません。もしこれができてしまうと、継承を行うことでいくらでもオブジェクトを造り変えることができることになってしまいます。派生先からはパブリックなメンバのみを使ってアクセスします。こうすることで厳しいデータ管理を行うことができます。

多態性(ポリモーフィズム)
 継承を使用することで、同属性のオブジェクトをどんどん作ることができます。これらのオブジェクトでは、同名のメンバ関数を作製することができます。例えば「猫」から派生された「三毛猫」「シャム猫」「チンチラ」の各オブジェクトに「毛色タイプ」メンバ関数を作製することができます。そしてこの場合、各関数は同じ引数を取り同じ戻り値を返すけれども、関数の中で行っていることは別ということにします。
 このとき、「猫」オブジェクトにも「毛色タイプ」メンバ関数があったら、どうなるでしょう。どちらの関数が呼ばれるか明確にはなっていません。それを防ぐために常に派生先の関数が存在するか調べ、適切な関数を呼び出してくれる機能があります。これを「仮想関数」といいます。

 「仮想関数」は継承元のメンバ関数、つまりここでは「猫」オブジェクトの「毛色タイプ」メンバ関数に指定します。指定されたメンバ関数が呼ばれると、そのオブジェクトがその継承元かそれとも派生先なのか、派生先ならどの派生先なのか調べ、そして適切な派生先のメンバ関数を呼び出してくれるというわけです。
 さらに「純粋仮想関数」と呼ばれる物は、実体を持っていない仮想関数です。このメンバ関数は、派生先で新たに作製されることが予想されるときに作製されます。

 こういった、オブジェクトを派生することでその属性がどんどん変わっていくことを「多態性」と呼び、さらに「オブジェクト的」な扱いを行うことができるわけです。

オブジェクト指向の利点と欠点
 オブジェクト指向の良さは、一度作ってしまえばデータの管理が楽になるということです。これはオブジェクト指向に限らず、プログラミングにおける多くの部分で言えることです。かっちりとしたシステムを構築することで、安定性が約束されるというわけです。
 しかし、そこにたどり着くためには様々な難関が立ち塞がっています。オブジェクト指向の場合、「概念」「実装方法」「管理」のみっつをしっかりと学ぶ必要があります。

 「概念」とは、それぞれの機能の本来の意味です。継承やアクセス指定はどのような必要性から生まれて、どういうことのために使うべきなのか。今回の講座ではこの部分を中心に解説したつもりです。
 「実装方法」は、それぞれの言語やアーキテクチャーでの、いわゆる文法的な部分での意味です。「オブジェクト指向」はプログラミングにおける多くの部分で存在し、そしてその場その場で違います。それらを正確に理解して、必要な方法をとることが求められるでしょう。
 「管理」は、作っている途中、そして作ったあとのメンテナンスです。例えば、あまり考えないで作っているとメンバを手当たり次第パブリックにしがちです。本当に必要なものだけをパブリックにし、あとはプライベートにすることが大事です。そして、「関数によるパッケージ化」を忘れずに使用して、さらにデータ管理を徹底することが大事になってくるでしょう。

 とにかく、「データ管理」。これが一番大事です。そのための便利な道具が「オブジェクト指向」であり、もしなければ、ないなりに巧妙に管理する必要があります。実際、「コンパイラに引っかからないミス」のほとんどは「データ管理」の不手際に起因します。見やすく分かりやすい管理、これが大事だということを忘れないでください。

 さて、次回はついに最終回。プログラミングのまとめをしようと思います。

(C)KAB-studio 1998 ALL RIGHTS RESERVED.