memo/20051221
created 2005-12-21 modified 2007-04-25
多重継承
カーギルとウォルドの多重継承は要るか要らないかの議論の話。ウォルドの論文に出てくる継承3種類
- 実体継承(差分プログラミング)
- インタフェース継承(そのクラスを利用する側がメソッドを使えることが保障される)
- データ継承(...)
で
ワタシは今まで実体継承での多重継承って要らないよなーと思っていたのだが、「なくてもいいけどあると便利」な場面もあるかな、と思ってきた。
C++は無名の構造体メンバを使えない。C++で構造体階層のある1レベルを無名にしたいときは、そいつをメンバにするのではなく継承してしまうという方法がある。
もちろんそのときの基底クラスはvirtualなメンバなんか一切なくて済むはずで、そんなに重くなるとも思えない。
ということで、無名の構造体メンバを言語仕様として使えないから、実体の多重継承はアリにしておいてほしいと。
D言語では無名の構造体が使えるようなのでそれなら実体の多重継承はなしでもいい。
メモ追記。
実体継承
いわゆる差分プログラミングは、はっきり言ってぜんぜんソフトウェア工学に寄与しないということがわかってきたはずで、なんで上記のようなことを考えたのか思い出せない...えーと
たしかCのライブラリをC++化していて
構造体階層において
Entity と、それを作る Manager の関係があって、
数の対応が Entity = N個に対し Manager = 1個 だった。
もとのCライブラリでは
entity.c と manager.c があり、Entity のファクトリ機能(new Entityする機能)が entity.c に書かれていた。確かにEntityに関する機能だが、個数関係で言うとそれって Manager が持つ機能だよなぁ、と。
処理 | 実装ファイル | オブジェクトの数関係からつながりが深いのは |
---|---|---|
Entityの動作に関する処理 | entity.c | Entity |
Entityを作る処理 | entity.c | Manager。ここがねじれてる |
Managerの動作に関する処理 | manager.c | Manager |
Entiryを作る処理をmanager.ccに移動すると、流用もとの.cファイルとの差分管理が面倒になる。
そこで、
クラス | 実装ファイル |
---|---|
Entity クラス | entity.cc |
EntityFactory クラス | entity.cc |
Manager クラス | manager.cc |
Manager は EntityFactory を継承する、とした。そうすることによって、
entity = mgr -> efactory -> addEntity()
と書かずに
entity = mgr -> addEntity()
と書ける(無名のメンバのように)、だからイイジャン、とかそんなことだったと記憶する。
データ継承
ウォルド先生の言う「データ継承」はいまいちよくわからなかったのを覚えているが、あらためて考えを整理してみた。「すべてのメンバに対する処理」を行うメソッドを基底クラスで定義し、さまざまなデータ項目を持つクラスを派生クラスとして作るというものだった。と思う。
(基底クラスとして「共有メモリ」とか「ファイル」とか「DBテーブル」を作って、さまざまなデータを持つクラスをその派生クラスとして作る。基底を変えると保存場所を変えられるとかなんとか)
つまり、メタ機能っていうか言語機能の一部(リフレクション?)を使いたい場面で、それを継承を使って実現する技法のことを言っていたのだと思う。
データを持つ構造を AAA という名前にするとして
class AAA : 共有メモリ
class AAA : ファイル
class AAA : DBテーブル
でもこれじゃ1個のコンパイル単位に同じ名前で共存はできないよ。
だから結局
class AAA_共有メモリ : 共有メモリ
class AAA_ファイル : ファイル
class AAA_DBテーブル : DBテーブル
となるわな。
それで、問題世界のほうで継承を使いたいときに
class AAA_共有メモリ : 共有メモリ, AAABase
class AAA_ファイル : ファイル, AAABase
class AAA_DBテーブル : DBテーブル, AAABase
となるということで、これを先生は「多重継承が必要な場面のひとつのデータ継承だ」というのだけど。うーむ。
まぁ多重継承の意味論の整理ということで項目に並べるのは有益だけど、実際には説得力は薄いな。
正味な話、この例では
class AAABase_共有メモリ : 共有メモリ
class AAA_共有メモリ : AAABase_共有メモリ
とやれば多重継承なんか要らないし。
言語機能に多重継承が必要である理由としては「インタフェース継承」ほど強くない。
で、その「データ継承」って、現在では継承ではなくテンプレートでやるのが普通なのかも、と思った。
ライブラリ作者は
class 共有メモリ<T>
class ファイル<T>
class DBテーブル<T>
を作って、Tをユーザに書かせると。