Entry

継承は基底クラスに共通する処理をまとめるだけの機構ではないという話

2010年02月22日

オブジェクト指向の入門書を読んでいると,継承について謎の説明があります。つまりこういうもの。

継承は,各クラスに共通する機能を基底クラスにまとめることによって,クラスの再利用を図るための機能です。また,基底クラスを継承することによって,派生クラスは基底クラスの機能を(再コーディングすることなく)容易に拡張することができます。つまり,継承はクラスの再利用を図るための機構だと言えます。

まあ……そゆ側面もあるとは思うんですけどね。それを言うなら,委譲(delegate)を使えばいいじゃんか,と思うわけですよ。どういうことかというと,要するにですね。

// 基本機能を持つクラス
class A {
public:
  A();
  virtual ~A();
  // ...
};

// A を継承する
class B : public A {
public:
  B();
  virtual ~B();
};

とかする必要はなくて,

// 基本機能を持つクラス
class A {
public:
  A();
  virtual ~A();
  // ...
};

// A の機能を C に持たせる
class C {
public:
  C();
  virtual ~C();
private:
  A a_;
};

と,使いたい機能をメンバに持たせてしまえばいいじゃんか,ということです。これだって,共通する機能をひとつのクラスにまとめられるし,基本機能の利用側(クラスC)は,A の機能を使いながら固有の機能を用意に拡張できます。

なんつかですね。継承についてこういう説明をしているのは,継承のメリットについてちゃんと理解してないんじゃないだろうかと思うわけです。これだけの目的のために,継承なんか無理矢理使わなくてもいい。

じゃ,どういう場合に継承が必要なのか。ひとつの理解の仕方として,次のような考え方を挙げてみます。

  • オブジェクトのインターフェイスを保証するため。
  • 「将来的な」拡張性を保証するため。

まず最初の点について。これは他のエントリでも書いていると思うんですけれど,基底クラスに書いたインターフェイスを指定することで,派生クラスが必ず持っているメソッドを保証することができます。例えば,次のような継承関係がある場合,

class D {
public:
  D();
  virtual ~D();
public:
  // インターフェイス
  virtual int exec() const = 0;
};

class E : public D {
public:
  E();
  virtual ~E();
public:
  // インターフェイス
  virtual int exec() const;
};

次のような関数の呼び出しができるようになります。

void
func(D* d) {
  d->exec();
}

int
main() {
  D* d = new E();
  func(d);
  return 0;
}

関数 func に渡ってくるオブジェクト D へのポインタは,実際,クラス E のオブジェクトだったりします。通常,型が異なるオブジェクトを渡されると,関数 func はそのオブジェクトが関数 exec を持っているか知らないわけだけれど(もちろん,プログラマは知っているだろうが),ここでは E が D を継承しているという事実でもって,関数 exec を持っていることが保証されます。is-A 関係というのは,こういうことで,概念的にそういえるということだけでなく,「基底クラスの範囲内で,機能的・情報的にまったく同じものとして扱える」ということだったりします。

さらに,このことを敷衍させると,関数 func が扱えるオブジェクトの種類について,将来的に拡張する可能性を残しておくことができることでもあります。つまり,上の関数 func が仮に以下のように定義されていた場合,この関数は,クラス E (とその派生クラス)のオブジェクトしか受け入れることができません。

void
func(E* e) {
  e->exec();
}

あえて親のクラス(基底クラス)へのポインタで受けることで,将来的に func の利用範囲を広げることができるというわけです。コードを使いまわすというのは,基底クラスのコードを使いまわすというよりは,そのクラスにまつわるあらゆるコードを抽象化したまま扱えるということでもあります。

もちろん,こうした考え方は,C++ や Java のような型付け言語だからこそ言えることだったりします。型付け言語じゃない言語で,継承する意味があるかっつと……とうなんだろ。型付け言語ほどはないんじゃないかと思う。というのも,型付け言語の場合は,オブジェクトの抽象性を型とその継承関係で(も)表現するのに対して,非型付け言語のクラスは,いくら継承関係を作ったとしても「オブジェクトの利用側に関する限り」すべて横並びとしてしか扱えないからです。これは以前も書いたとおり。

とま,いずれにしても,基底クラスに共通する処理をまとめて……とかいった説明をしている入門書は,ちとにんともかんともな感じがするわけです。なんとかならんのだろうか。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN