あめだまふぁくとりー

Boost.Graphとかできますん

純粋仮想関数の頃には…

なにごとも何故そうなっているのかを考えるのが重要である.その"何故"を理解することで今まで見えなかったことが鮮明に見え,新しい世界が開けるのかもしれない….

と言う訳で本題はこれ↓

class Hoge {
  public:
    virtual void method() = 0;
};

そう!純粋仮想関数の構文である.Javaをちょっとでもかじった人から見たら「マジキメぇwww」「あげぽよー!!」という言葉が出てくるかもしれない.この構文が気にくわない人はちらほらいるものである.
でも「関数methodは存在しない.つまりこの = 0 はぬるぽだよ.ガッ」と言うと何だか普通に見えてこない?見えてくるでしょ?見えてきたよね?まだ見えねぇの?見えんだろうが!
しかしこのように言う人がいるかもしれない.「ぬるぽは NULL だろ.ガッ」
確かにC言語のときはぬるぽはNULLだった.ガッ.しかし今はC言語の話をしているのではなくC++の話をしているのである.我々C++初学者はマクロは悪であり,それを使う人間は○してもよいと一同に教えられている(魔(クロ)界の住人は人間ではない!).よって = NULL ではなく,= 0 を使うべきなのだ.これでもう納得してもらえただろう.上の構文はその意味をきちんと表現する構文なのだと.
じゃぁ,これはなんなの?関数が存在するじゃないか

class Fuga {
  public:
    virtual ~Fuga() = 0;
};
Fuga::~Fuga() { }

何ということでしょう!関数(上の例はデストラクタですけど)が存在するにも関わらず = 0 としている!一体これはどういうことなのか!いつからC++はこんなクソな構文を採用してしまったのか!

…みたいなことを考えていたわけです.で,仮想関数テーブルにヌルポインタを代入しているという結論に至りました.上の例だとテーブルのデストラクタの欄にぬるぽを代入しているということ.ガッ.こうするとFuga::~Fuga()のような静的な呼び出しはできるけど動的な呼び出しはできなくなりますよね.こう考えると純粋仮想関数を持つクラスがインスタンス化できないのも納得がいくというか.例えば

Fuga* fuga = new Fuga;  // コンパイルはできないよ!
delete fuga;

を考えたときにオブジェクトを削除する時に動的な呼び出しになるので呼び出すことができない.じゃぁヒープ上に作るのだけ禁止しろよというかもしれないけどデストラクタ以外に純粋仮想関数を持つ場合に,参照を受け取る関数にオブジェクトを渡すとまずいのでやっぱりアウト.参照に渡すのも禁止ねとか言われたら,Fugaの派生オブジェクトは渡せるのにFugaは渡せないという一貫性のないものになってしまう.
一方派生オブジェクトからは常にFugaのデストラクタは静的に呼び出されるので仮想テーブルなんて関係ないね!
また,下のようにvirtualがついてない,つまり仮想テーブルに存在しない関数の場合はコンパイルエラーになるからうまく納得できる.

class Foo {
  public:
    void method() = 0; // エラー
};

まぁここに書いた内容は自分が考えたことなんであっているかなんてのは知りません.つまりISO/IEC 14882:2003(2011でもいいのかな?)が欲しいですね