原文: http://onigiri.s3.xrea.com:8080/delphi/index.php?%A4%CA%A4%BC%A5%C7%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF%A4%CF%B2%BE%C1%DB%A4%CA%A4%CE%A4%AB
超基本ではありますが。
塚越本持ってる人まとめてくれないかな(´∀` )
Delphi相談室その8 http://pc3.2ch.net/test/read.cgi/tech/1037146781/n108
108 :COLOR(green){デフォルトの名無しさん} :02/11/30 02:08
type TTest = class(TObject) public constructor Create; destructor Destroy; override; end;
http://www.acesekkei.com/dp_03.htm
↑ここのサンプルなどを見ると、TObjectから直接派生したクラスで、コンストラクタやデストラクタを自前で書く場合、デストラクタの方だけoverrideしてinheritedを呼んでいます。
なぜコンストラクタとデストラクタで書き方が違うんでしょうか?
TObjectのCreateは静的メソッドなのでoverrideできないのはわかるのですが、なぜあえてTObjectのCreateとDestroyは違うディスパッチの設計になっているのでしょうか?
解説していただけるとありがたいです。
解説
私は「塚越本」を読んだことはありませんが、仮想デストラクタと[[仮想コンストラクタ]]の話は OOPL ではよく出てくる話なので、以下にまとめてみます。つっこみよろしく。
提示されている TTest の Create コンストラクタが静的で、Destroy デストラクタが仮想なのは、直接には親クラス(および一般的には祖先のクラスの)TObject の Create コンストラクタが静的メソッドで、かつ Destroy デストラクタが仮想メソッドだからです。
一般にクラス毎に終了処理の内容は異なるので、デストラクタは仮想メソッドにする必要があります。デストラクタを仮想メソッドにすると渡されたインスタンス固有のデストラクタが呼ばれ、クラス固有の終了処理を行うことができます。 デストラクタが仮想メソッドでない場合は、コンパイル時に決定される特定のクラスの特定のメソッドが呼ばれることになります。この場合、オブジェクト指向言語の利点としての多態型に対して適切な終了処理を呼ぶことが困難となります。
オブジェクト指向言語を特徴づける点として、インスタンスとメソッドの関係があります。メソッドは関数/手続きとは異なりインスタンスに従属します。原則、インスタンスが存在しないとメソッドを呼ぶことはできません。その代わりにメソッドが必要とするデータをインスタンスの中に閉じこめることができるため、クラス全体で見た場合に独立性や可搬性が高まることになります。
コンストラクタを(通常は)仮想メソッドにできないのは、このオブジェクト指向言語の特徴から出てきます。コンストラクタとはインスタンスを生成するメソッドです。コンストラクタを呼ぶ時点ではインスタンスはまだ作成されていないのですから、仮想メソッドにすることはできません。
コンストラクタ以外のすべてのメソッドは仮想メソッドとして定義することができます。実際、すべてのメソッドが仮想メソッドにしかできないオブジェクト指向言語も存在します。しかし Delphi では、主に性能上の問題から静的メソッドをデフォルトにし、プログラマの判断で仮想メソッドを選択できるようにしています。
まとめると、
- コンストラクタはインスタンスが無くても動かなければならないので、仮想メソッドにはできない。
- デストラクタは多態をサポートするために仮想メソッドで無ければならない。
- その他のメソッドは仮想メソッドにしておけば、継承したクラスでカスタマイズが可能。ただし性能上のトレードオフを考慮すること。
という訳で、「なぜデストラクタは仮想なのか」という問いに対しては、「多態をサポートするため TObject.Destroy が仮想メソッドになっているから」となります。
「なぜデストラクタとコンストラクタは異なるのか」という問いには「インスタンスが無くても呼べなければならないので、コンストラクタだけ特別」ということになります。
実は、Delphi には、メタクラスとしてのクラス参照型が存在するため、クラス参照型の変数をインスタンスと見なして[[仮想コンストラクタ]]を作ることができます。Classes.pas の TCollection の定義を参照してください。
C++Builder
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.5
C++ FAQ って書籍ではない和訳無いの?
お前ら
- 塚越本をまとめる必要は無いと思うが。 — SIZE(10):2002-11-30 (土) 18:54:24
- 本を転載しちゃまずいけど、お題を拾うにはいいかと思って。 — SIZE(10):2002-11-30 (土) 19:30:15
- 一般論として、デストラクタは仮想/Virtual なんです。それで良ければまとめられますし、google で検索すれば良い説明が見つかるでしょう。 — SIZE(10):2002-11-30 (土) 19:36:08
- TObject.Destroyがvirtualなんだからそれを継承したクラスがoverrideするのは当然。これはメソッド全般の話。 — SIZE(10):2002-12-01 (日) 11:02:44
- 乙です。明日あたり初心者向けの解説を追加してみようかな。 — SIZE(10):2002-12-01 (日) 22:56:40
- "適切な終了処理を呼ぶこと" ができない事をまず静的メソッドがどうやって動くかをコードを交えて説明した方が良くないかな?下記の例で、aはTTestBでCreateされているけれど、a.Methodで動くのはTTestA.Methodの方だよね、と。これと同じ事がDestroyで起きると、例えば資源の解放漏れが起きるでしょ、という話の展開の方が分かりやすいんじゃないかな、と思う。
type TTestA =class procedure Method; end; TTestB =class(TTestA) procedure Method; end; var a: TTestA; begin a:=TTestB.Create; a.Method; a.Free; end; procedure TTestA.Method; begin ShowMessage('TestA Method') end;
procedure TTestB.Method; begin ShowMessage('TestB Method') end;
- 説明が難しいな、問題を整理しようか。 1.仮想/静的メソッドの違いは何か。 2.何故Destroyは仮想であるのか。 3.何故Createは仮想でないのか — SIZE(10):2002-12-01 (日) 23:47:46
- 問題1はクラスの説明のところで済んでいるものとしてこのページを書いちゃっていいと思う。 — SIZE(10):2002-12-02 (月) 00:29:15
- 激同。ある程度の予備知識は前提としないと、解説ができなくなる。 — SIZE(10):2002-12-02 (月) 08:02:23
- あと、できればほんとの初心者の感想を聞きたいな。 — SIZE(10):2002-12-02 (月) 08:02:57
- ほんとの初心者はコンストラクタ、デストラクタが何かさえわからんだろ。 — SIZE(10):2002-12-02 (月) 14:57:59
- そーゆーこと言ったら毎回すべて説明しなきゃいけなくなるので… 一応前向き/生産的にやっていきましょうよ。 — SIZE(10):2002-12-02 (月) 18:49:10
- 元ネタ振った奴を連行して来い — SIZE(10):2002-12-02 (月) 21:02:28
|