皆さんはとっくにお気付きかもしれませんが,本連載で使用してきたBorland C++ CompilerはC言語のコンパイラではなく,「C++言語」のコンパイラです。

 C++言語はC言語にオブジェクト指向的な拡張を施したプログラミング言語で,Bjarne Stroustrup氏によって仕様が作られました*1。C++言語はC言語の上位互換なので,C言語の言語仕様で記述されたプログラムでもコンパイルできたわけです。

 筆者は,C言語を理解された方は,次にオブジェクト指向の概念を勉強してほしいと思っています。今やオブジェクト指向は必須の知識ですし,システム設計にもデータベースにもオブジェクト指向の考え方は広まりつつあるからです。

 そこで最終回の一回限りですが,オブジェクト指向の概念や機能をC言語に「のっけた」C++言語で,オブジェクト指向プログラミングの世界をのぞいてみましょう。

オブジェクトはプロパティとメソッドを持つ

 オブジェクト指向は,現実世界に実在するモノをオブジェクトとしてモデル化し,プログラムに取り込もうとするアプローチです。C言語などの手続き型言語ではデータと手続きは別々に存在し,プログラムで指定した順に手続きがデータを処理していました。一方,オブジェクト指向では,オブジェクトがデータと手続きを持ちます。オブジェクト指向の用語では,このデータのことをプロパティ(属性)またはメンバー変数と呼びます。そして,手続き(振る舞い)のことをメソッドまたはメンバー関数と呼びます。

 漠然とした話になってしまいましたね。目覚まし時計で考えてみましょう(図1)。目覚まし時計には「現在の時刻」や「ベルを鳴らす時刻」というデータがあります。これが目覚まし時計のプロパティです。目覚まし時計のメソッドは,「時を刻む」こと,「目覚まし時刻を合わせる」こと,「現在の時刻を合わせる」ことです。もちろんほかにも製造メーカー,製造年,素材などのプロパティや,針を回すなどのメソッドがあります。しかし,モデル化しオブジェクトとしてプログラムに取り込むときは,そのプログラムで扱う必要があるプロパティやメソッドだけに注目して,関係のないものは無視します。現実にあるものをすべて写し取ろうとしゃくし定規に考える必要はありません。

図1●「目覚まし時計」というオブジェクトを考えてみよう
図1●「目覚まし時計」というオブジェクトを考えてみよう

 また,オブジェクトに対し送る指示や設定のことをメッセージと呼びます。例えば,目覚まし時計オブジェクトに「6時45分にベルを鳴らせ」というメッセージを送ることを,メッセージ・パッシングと呼びます。

クラスをインスタンス化して実体=オブジェクトを作る

 ここまでで,目覚まし時計のプロパティとメソッドを考えてみました。これは,見方を変えると目覚まし時計というオブジェクトの(概念的な)基本設計に相当します。この基本設計(ひな型)のことを,オブジェクト指向では「クラス」と呼びます。そして,クラスからメモリー上に実体(インスタンス=オブジェクト)を作る(もちろんコード上で)ことを“インスタンスの生成”と呼びます。

 インスタンスの生成などというと難しそうですが,C言語の構造体が実は型宣言であり,構造体型の変数をメモリー上に作成して利用するのと考え方は同じです(図2)。実際のプログラムでは,一つのクラスから複数のオブジェクトを生成して処理を行います。誤解を恐れずに言うならば,「クラスからインスタンスを生成する」――最低限これだけできれば,オブジェクト指向プログラミングと言えるわけです。

図2●基本設計であるクラスから,オブジェクトを生成する
図2●基本設計であるクラスから,オブジェクトを生成する

オブジェクト指向の三本柱はカプセル化,継承,多態性

 オブジェクト指向プログラミングの目的は,大規模なプログラムの複雑さを軽減し,効率的なプログラミングを実現することです。そのための三つの柱が,カプセル化,継承,多態性です。

 まず,「カプセル化(情報隠ぺい)」から解説しましょう。カプセル化とは外部に公開する必要のない(もしくは,公開したくない)プロパティやメソッドをオブジェクトの外部から参照できないようにすることです。不用意な値の変更を防止して安全性を高めようというわけです。

 例えば,目覚まし時計がベルを鳴らす時間を,プログラムのどこからでも書き換えることができたらどうでしょう。予期せぬ時刻に目覚ましが鳴ったら困りますよね。そこで,こういうときは,“目覚ましの時刻”を隠ぺいして,“目覚ましの時間をセットするメソッド”を公開します。時刻を変更したいときは,時刻を直接変更するのではなく,メソッドを使って変更します。そうすれば,おかしな時間に鳴るようなことがないよう,メソッドの中で不正な値を除外でき,安全性を高められます。目覚まし時計オブジェクトを利用するときは「目覚まし時間を○○時に設定してくれ」というメッセージを送ればいいのです。

 オブジェクト指向プログラミングの2番目の柱が「継承」です。継承とは他のクラスのプロパティやメソッドを引き継いで新しいクラスを定義することです。継承を使うと,既存のクラスから新しいクラスを作成し,新たに機能を追加したり変更したりできます。継承される(継承元となる)クラスを基底クラス,親クラス,スーパークラスなどといい,新たに作成するクラスを派生クラス,子クラス,サブクラスなどといいます。

 親がいて子を作るのが継承ですが,設計段階では逆の順序で考えます。例えば,犬クラスを作りたいとしましょう。犬クラスを作る必要があるのだから,きっと猫クラスや牛クラスも必要だろうと推測できます。実際,犬,猫,牛には共通点が多くありますね。鳴きますし,4本足で歩きます。そういった共通点に注目し,親クラスを導き出します。つまり,親から子ではなく,子から親を考えたわけです。この作業を汎化と呼びます。

 図3の場合,汎化を行うと「動物」という実際には存在しない抽象的な親クラスがあって,そのプロパティや属性を引き継いだ犬クラス,猫クラス,牛クラスが子クラスとして定義されます。難しく言うと設計段階での汎化が,プログラミング段階での継承になるのです。この場合,犬は動物であり,猫も動物ですから,クラス間の関係は「is_a関係(bはaである)」もしくは「a_kind_of関係」にあるということになります。継承を使うと,基本機能に差分機能だけをプログラミングすれば拡張できるので,プログラミングの開発効率を上げることができます。

図3●子クラスから親クラスを考えることを「汎化」と呼ぶ
図3●子クラスから親クラスを考えることを「汎化」と呼ぶ

 さて,オブジェクト指向プログラミング三つ目の柱が「多態性(ポリモーフィズム)」です。多態性とは,同じ名称で指示される操作が,様々な対応や様相を実現するという特性です。引数の数や型が異なる同一名のメソッドを複数作る「オーバーロード(多重定義)」と,親クラスで定義されているメソッドを,サブクラスにおいて同じ名前で定義し直す「オーバーライド(再定義)」で実現できます。

 少し乱暴な言い方をすると,犬に「鳴け」というメッセージを送ったら「ワン」と鳴くのは当たり前ですが*2,仮に小屋の中に犬か猫か牛か,どの動物がいるのかわからない状況で「鳴け」というメッセージを送ったら,その動物が猫ならば「ニャン」と鳴き,牛ならば「モー」と鳴くのが多態性です(これはあくまで「たとえ話」と割り切ってください)。

 手続き型言語だと,コンパイル時に呼び出す関数が決まってしまいますよね。しかし,多態性を利用すると実行時に動的に処理するオブジェクトを決めることができるので,柔軟なプログラミングが可能になるのです。

 説明ばかりで嫌になってきました。ここからはサンプル・プログラムで考えていきましょう。複雑そうに思える概念もコードに落とし込めば理解が早くなりますから。