C++の多態

# 今日からソフト屋モード。

C++の多態は大まかに2系統の表現方法があります。
一つはJavaや.NET等と同じ所謂"オブジェクト指向"な仕組み(継承とvtblによる動的な多態)、
もう一つはC++特有のtemplateによる静的な多態です。

今日は(続くかどうかわからない)C++シリーズの第一弾として、C++(に限らず)継承を使うときの有名なセオリーを軽く紹介します。

リスコフの代入則

public継承を使う場合は、"派生クラスを親クラスとして扱っても不都合が生じない"ように設計しなければならない。
例えば、テキストボックス(==入力と表示が一致している)を実装しているクラスをpublic継承して、入力が自動的に大文字に変換されるテキストボックス(==入力と表示が一致しない)を実装してはいけない。
この場合は、テキストボックスをフィールドとして持ち(合成,composition)、転送関数を並べて実装するか、入力そのものをフックしなければならない。
private継承を使う場合は、使う前にそれが合成では実現できないかをよく吟味する。private継承が必要な典型的な例は、protectedメンバにアクセスする必要がある場合と、empty-base optimizationが有効な場合。

NVI(Non Virtual Interface)イディオム

派生クラスにオーバーライドさせるメソッドは、public non-virtualの転送関数から呼ばれるprotected virtualメソッドにせよ。

class base {
public:
  int some_interface() { return do_some_action(); }
protected:
  virtual int do_some_action();
public:
  virtual ~base();
};

class derived: public base {
private:
  int do_some_action();
};

仮想関数は非公開でも問題なくオーバーライド可能ですし、計測コード等を埋め込みやすいという利点もあります。
親クラスの振る舞いをなぞりたいなら、通常通り

int derived::do_some_action() {
  return base::do_some_action();
}

してしまえばOKです。

継承を使うときのデストラク

継承を使うとき、親クラスに仮想関数があるならデストラクタはpublic virtualに。仮想関数が無ければ、protected non-virtualに。
前者は動的な多態を目的とした継承で、後者は実装の再利用を目的とした継承です。
どちらにも当てはまらない場合は、本当に継承を使う場面かどうか再チェック。


走り書きでしたが、これら(ともっと沢山の)の項目がGoF本や(More) Effective C++などに書かれているので、読んだこと無い人は今すぐ本屋へ行きましょう。