値セマンティクスとオブジェクト指向

STLの本を読んだ。

C++だから当然なのかもしれないが、コンテナの操作のベースにあるのが「値セマンティクス」であることに、違和感を感じる(すなわち、必然性を納得していない)。

オブジェクトへのポインタではなく、オブジェクト自体をコンテナに入れるのはコストがかかる(例えば、ベクタのメモリの再配置を想像したら気の遠くなるような処理が走ることがわかる)。それに、値セマンティクスのコンテナは、「オブジェクトをまとめる」ことを考えた場合、自然なモデリングとはならない(コンテナからはオブジェクトのコピーしか取り出せないので、それを使ってオブジェクト本体には作用できない)。

というわけで、僕がSTLを使用する場合は、コンテナにはなるべくオブジェクトのポインタを入れるようにしたいと思っている。メモリリークを防ぐために、スマートポインタを使用することになるだろう。

ちなみに、STLをデザインしたステパノフは、本の序文の中で次のように言っている。

部分と関係とを混同することは、オブジェクト指向言語およびライブラリではよくあることで、現実世界をモデル化するにあたり概念の混同をきたす面たる原因であり、同時にガベージコレクションがどうしも必要になる大きな理由だと思っている。STLオブジェクト指向ではない−−大域汎用アルゴリズムの使い方だけでなく、もっと重要なことは、オブジェクトを部分として所有することとオブジェクトをポイントすること、これら二つの概念をはっきり分けている事実において。

この人は一つのことを言うのに、おまけを付けて二つのことを言ってしまう人のようだ。重要なところを抜き出すと、以下のようになる。

部分と関係とを混同することは、オブジェクト指向言語およびライブラリではよくあることで、現実世界をモデル化するにあたり概念の混同をきたす面たる原因である。STLオブジェクト指向ではない。オブジェクトを部分として所有することとオブジェクトをポイントすること、これら二つの概念をはっきり分けている事実において。

「現実世界のモデル化」というのがちょっとわかりにくい表現だ。というのも、オブジェクト指向モデリングの例として、現実世界をイメージさせる例が良く出てくるので。そのような世界では、物事を簡単にコピーはできない。例えば、yhayashiという名前の人間がいるとしたら、その人間をコピーすることはできない。一方、yhayashiという名前は単なる文字列なのでいくらでもコピーすることができる。

値セマンティクスでコンテナに人間を入れてしまうと明らかにおかしなことになる(一人の人間が複製されることになってしまう)。一方、ポインタセマンティクスでは、ポインタしかコピーされないので問題はない。ステパノフが批判しているのは、「実際にはコンテナに人間を入れているわけではないのに、人間を入れているふりをするな」ということなのだろう。そして、彼の言う「現実世界のモデル化」というのは、「人間を指す名前をコンテナに入れる」ということになるのだと思う。

ところで、僕は学生のころ、Javaを始めて見たとき、ポインタがなかったことに感動をおぼえた。Cの「値セマンティクス」はこれ以上ないというくらいエレガントだけれど、Javaは、これを「ポインタの値セマンティクス」で統一し、実はポインタも必要なかったということを証明してしまった(ように感じた)。当時は、ドエラい発想の転換をする奴がいるもんだと感心したものだ。

ステパノフの文を読んで、値セマンティクスとオブジェクト指向の関係について、そして、オブジェクト指向モデリングの際に無意識に行っている抽象化について改めて意識することができた。しかし、この辺の背景には、僕がまだ知らない事実が何かあるような気がしている。例えば、もっとも古い言語の一つであるLispは、オブジェクト指向ではないが、ポインタのコピーに基づいたセマンティクスを採用している。こう考えると、ポインタセマンティクスは、オブジェクト指向に限ったことではないようだ。さらに、C++オブジェクト指向の言語だけれど、値セマンティクスを採用している。なぜC++はそういうモデルを採用したのか。そしてその反響はどうだったのか?また、Javaは、C++の欠点を覆すために開発されたといわれるが、そのとき焦点となったことは何なのか。この辺の歴史について学べる本があれば読んでみたい。