デストラクタ
デストラクタ(英: destructor)は、オブジェクト指向プログラミング言語でオブジェクトを削除する際に呼び出されて後処理などを行なう関数あるいはメソッドのこと。C++やDelphiにおいてサポートされている。デストラクタは確実かつ安全なリソース管理を実現するための重要な役割を担う。
日本語では「解体子」という直訳が割り当てられることもあるが[1]、ほとんど使用されることはない。
なお、本項では類似概念であるファイナライザについても合わせて述べる。
C++[編集]
デストラクタはオブジェクトの寿命が終了するタイミング、メモリ領域が破棄される直前に自動的に呼び出される。具体的には自動変数(ローカル変数)ならばその変数が属するブロックを抜けた直後、静的オブジェクトならばプログラム終了直前、new演算子で生成したオブジェクトならばdelete演算子が適用された時である。主にコンストラクタで確保したリソースを解放するための処理が記述される。
派生クラスの場合は、まず派生クラスのデストラクタが呼ばれて派生クラスによる追加部分が解体されてから、基底クラスのデストラクタが順次呼ばれることでオブジェクトが解体される。基底クラスのポインタで派生クラスのインスタンスをポリモーフィックに利用する場合は、基底クラスのデストラクタを仮想関数にしなければならない(仮想デストラクタ)。これはポインタが参照するインスタンスをdeleteする際に呼び出されるデストラクタがポインタの型で決定されるため、基底クラスのデストラクタが仮想でない場合は、基底クラスのデストラクタだけが呼ばれて派生クラスのデストラクタが呼ばれないためである。基底クラスが仮想デストラクタを持っていれば、実際のインスタンスに応じて派生クラスのデストラクタが正しく呼び出される。
自動変数のデストラクタは、例外でブロックを脱出した際にも呼び出される。そのため、コンストラクタでリソースを確保し、デストラクタでリソースを解放するクラスを自動変数として生成することで、ブロック中のどこから例外が投げられてもリソースの解放が確実に行われる。このイディオムをRAII (Resource Acquisition Is Initialization) という。
デストラクタは例外を投げるべきではない。先に述べたようにデストラクタは例外伝播中にも呼ばれる可能性があるが、その時にデストラクタがさらに例外を投げると二重例外となり、プログラムの強制終了を招くからである。
デストラクタは、プログラマが定義しない場合にC++コンパイラが暗黙に生成するメンバー関数のうちの一つである。暗黙のデストラクタの仕様は「中身が空」で「非仮想」となっている。
デストラクタの名前は、クラス名の前に~
記号を付けたものと決められている。
ファイナライザ[編集]
ファイナライザ (finalizer) は、ガベージコレクタを持つ言語において不要オブジェクトが回収される前に自動的に呼び出されるメソッドである。
Java、Rubyなどに存在する。C++/CLIにはデストラクタとファイナライザの両方が存在する。C#にはファイナライザのみが存在するが、構文がC++のデストラクタに酷似しており、かつては「デストラクタ」と呼ばれていた[2][3]。
デストラクタと違い、ファイナライザはオブジェクトが不要になってもすぐには呼ばれるとは限らない。不要になってから実際に回収されるまでの間に、いつか呼ばれるというだけである。それは不要になった直後かもしれないし、遙か後になるかもしれない。さらにはメモリに十分な余裕があれば、オブジェクトが回収されずファイナライザが永遠に呼ばれないということさえありうる。
このようにいつ呼び出されるかわからない、呼ばれるかどうかすらわからないメソッドのため、確実に実行されなければならないような処理は一般的にファイナライザに任せることはできない。したがって、「ファイナライザによるRAII」は誤りである。ファイナライザで行えることは極めて限定されており、「リソースを解放し忘れていないか確認し、解放し忘れていれば解放する」のようなフェイルセーフ的な利用法がせいぜいである。リソースは不要になった時点で適切に解放されているべきであり、ファイナライザのチェックは保険に過ぎない。解放し忘れは本来はバグで、ファイナライザに頼る設計をしてはいけない。ファイナライザを誤って実装すると、脆弱性が生じることもある[4]。
以上のような不確実性に加え、ファイナライザの利用はガベージコレクタの性能を低下させることもあり、積極的に利用されるものではない。業務サーバーのような、少々のバグがあっても動き続けていなければ困るようなアプリケーションでは用いられることもある。
Java 9ではファイナライザが非推奨となった[5]。