実は本題と関係ないのですが、檜山さんが書いた文章に1つだけ個人的に引っかかるところがありました。
僕は、オブジェクトもthisもサッパリ理解できなかった
より引用
それで、一番わからないのが、thisとかselfに対するメッセージ・パッシング。「自分にメッセージを送る」って何よ、それ? 他人を動かすならともかく、自分ならサッサとやりゃいいじゃん。イチイチ「俺、お茶飲め」とか「私、ドア開けましょう」とかメッセージしないといけないわけか。
この問題ですが。
なぜ、自分に対するメッセージを送る必然性があるのか、という問い掛けは、ある意味でもっともに思えます。しかし、実際に必然性のあるシステムが存在するので、その仕様、あるいは実装をベースにすれば、なぜ必要なのかを説明できます。
そのシステムとは何か? §
オブジェクト間でメッセージをパッシングして動作するモデルを採用しているシステムというのは、実はみんなが使っているWindowsです。ほとんどのプログラム言語では、メッセージ・パッシング・モデルを隠蔽してしまうので、気付いていない人も多いかもしれませんが、生のWindows APIの世界では、ウィンドウとはまさにメッセージ・パッシング・モデルのオブジェクトとして実現されています。
実際、Windowsでウィンドウを生成するということは、ウィンドウのクラスをシステムに登録して(RegisterClaass)、クラスを指定してウィンドウのインスタンスを生成する(CreateWindow)という手順で行われます。また、この生のオブジェクト・モデルのレイヤーで、サブクラス化というテクニックも用いられます。
つまり、Windowsの世界では、1つのウィンドウは1つのオブジェクトであり、ウィンドウとウィンドウの間はメッセージを交換することによってコミュニケーションするのが基本的な仕組みとなります。(実際にはそれだけで割り切れるほど綺麗ではないが……)
以下、16bit時代のWindows APIを前提に説明します。なぜかといえば、32bitの方は詳細まで良く分かっていないから。もっとも、16bit時代の知識もいい加減さび付いているので、間違ったことを誇らしげに書いている可能性はありますけどね (汗。
非同期メッセージ送信の必要性 §
メッセージが同期的に送信される場合と、非同期に送信される場合では必然性に差があります。
ここではこの2つを分けて扱います。
まず、PostMessage APIを用いて非同期にメッセージを自分自身に送信する場合です。
PostMessage APIを用いてメッセージを送ると、そのメッセージはキューに追加されます。その結果、送信されたメッセージは未来のいつかの時点、キューの処理が進んでそのメッセージが取り出される時まで遅延されます。
このような遅延は、いろいろな用途で有用です。
たとえば、ウィンドウの再描画はできるだけ遅延させた方が有利です。なぜかといえば、内容の変化が続いている状況で、小さな変化が起こるごとにいちいち再描画を発動させていたら、貧弱なマシンでは再描画だけでCPUパワーが食いつぶされてしまうからです。また、パワーが十分であっても、不必要に書き換えが多いと表示がちらついたりします。
もちろん、ウィンドウの再描画要求はそのウィンドウ自身が発生させることがありますが、その場合でも、即座に描き直すのではなく、再描画が必要なリージョンを指定して再描画要求はキューにセットさせる(InvalidateRect API呼び出し)ように作成するのが基本です。
(ちなみに、再描画の要求メッセージだけは他のメッセージよりも特別扱いされていて、できるだけキューの後ろの方に移動させるようになっている)
このようなケースでは、自分自身へのメッセージ送信は、自分の未来の予定をスケジュール帳に書き込む行為に相当すると言えます。
同期メッセージ送信の必要性 §
SendMessage APIによる同期メッセージ送信は、実質的に関数呼び出しに非常に近い性格のものです。
しかし、別プロセス上のウィンドウに送った場合は、そのプロセスのコンテキストで実行されるため、単なる関数呼び出しでは実現できない付加価値が発生します。
では、自分自身に対してのメッセージ送信はどうでしょうか?
関数呼び出しで置き換えられるでしょうか?
とりあえず、少なくとも3つは置き換えられないケースが考えられます。
ケース1: 関数形式のインターフェースが存在しない場合 §
全てのメッセージは、ウィンドウ関数と呼ばれる関数で処理されます。通常、ウィンドウ関数は内部にswitch文を持ち、メッセージ番号(を定義したマクロ)によって処理すべき機能を仕分けします。つまり、メッセージと1対1で対応する関数は存在しません。ですから、一般論としてはメッセージ送信を行う代わりに関数呼び出しを行うことはできないわけです。
もちろん、switch文で個々のメッセージに対応する関数を呼び出すようにコーディングすれば対応する関数を直接呼び出せる訳ですが、それはそのようにコーディングした場合にのみ可能となることです。
ケース2: サブクラス化に備える §
ウィンドウのクラスやウィンドウ自身は、常にサブクラス化の対象になり得ます。
サブクラス化が行われると、サブクラス化した側がメッセージを横取りして処理してしまうことがあります。
つまり、あるウィンドウ関数が自分の知っている内部関数を直接呼び出した場合と、同期メッセージ送信を行った場合では、結果は同じにならない可能性があるわけです。
サブクラス化による拡張に備える場合には、自分自身が持つ機能を呼び出す場合でも、メッセージを同期送信する必要があります。
そして、ウィンドウに対して作用する多くの便利ツールとの整合性への要求から、ウィンドウを管理するためのメッセージ群は正しくサブクラス化による拡張に備えねばなりません。
ケース3: サブクラス化で使う §
ウィンドウのサブクラス化を行うコードを作成するケースでは、たいていの場合、ベースクラスの内容はブラックボックスです。外部に対して安全に公開されているインターフェースがメッセージしかないこともあります。
そのような場合に、サブクラスからベースクラスの機能を呼び出すには自分自身への非同期メッセージ送信を使うしかありません。
これらのケースでは、オブジェクトを人間に喩えることが分かりにくさを生んでいるように思えます。人間はサブクラス化されることがないので、サブクラス化に関する必要性が見えにくくなります。
余談 §
メッセージ・パッシング・モデルのトレーニングとして、Windowsの生APIによるプログラミングというのはアイデアとしてありかもしれません。
その最大の長所は、世の中に当たり前のように溢れているWindowsパソコンと、それでプログラミングするためのC言語処理系があれば、容易に始められること。
短所は、Windowsのメッセージ・パッシング・モデルを、一般論としてのメッセージ・パッシング・モデルを取り違えてしまう可能性があること? これはけっこう痛い短所かな。