参考: デストラクタを呼ばずに再構築 - melpon日記 - HaskellもC++もまともに扱えないへたれのページ
C++ では、動的に確保したメモリ領域にオブジェクトを構築した場合、
確保した領域を解放する前に、デストラクタを呼び出す必要があります:
// 何らかの理由で new 以外の方法でメモリ確保する必要がある場合 // メモリ確保し、オブジェクトを構築する(todo:例外安全) void* const vp = allocate_memory( sizeof(T) ); T* const p = ::new(vp) T(); // 何らかの処理 // デストラクタ呼び出し p->~T(); // 解放 free_memory( vp );
普通に C++ を使うだけであれば、動的メモリ確保は new/delete を使えばいいし、
そもそも unique_ptr とか shared_ptr とかで管理するのが普通なので、
このあたりのことは特に意識する必要はありません(し、意識しないようなコードを書くべきです)。
しかしながら、時々、意識せざるを得ない場合があります。
僕の場合は、 Lua でユーザデータを作ろうと思ったときに、この問題にぶち当たりました。
Lua のユーザデータは、メモリの解放処理が Lua の GC によって行われるので、
C++ 側では解放のタイミングを掴むことが出来ません。
void* const vp = lua_newuserdata( L, sizeof(T) ); T* const p = ::new(vp) T(); // vp は Lua の GC によって管理される // いつ領域が解放されるかは分からない!
この問題は、メタテーブルの __gc にデストラクタを登録することで対処できますが、
処理速度的に、出来る事ならばメタテーブルは使いたくない、というのは自然だと思います。
少なくとも、 C でユーザデータを使う場合には、特別な破棄処理なんて必要ないので、
ゼロオーバーヘッドを原則としている C++ で、それが出来無い道理はない筈です。
そう思って調べたところ、やはりというか、
C++ でもデストラクタ呼び出しが必須でない場合はあるようです。
その条件は、上に挙げた めるぽん先生の日記によれば、
このどちらかであれば、明示的にデストラクタを呼ぶことなく、
勝手に領域の再利用/解放を行っていい、とのこと(規格は N3092 の 3.8 を参照)。
で、この条件について、後者の「副作用のない non-trivial なデストラクタを持つ」というのは判定が難しいですが、
前者の「 trivial なデストラクタを持つ」というのに関しては、
boost/tr1 の type_traits に has_trivial_destructor というメタ関数が存在します。
後者に関しては、前者に比べて数は遥かに少ないので、特に気にする必要もないですが、
もしどうしても最適化したいのであれば、自前で
template<typename T> struct has_nosideeffect_destructor : boost::has_trivial_destructor<T> {}; struct hoge { ~hoge() throw() {} }; template<> struct has_nosideeffect_destructor<hoge> : boost::true_type {};
のようなメタ関数を作れば良いと思います。
コンパイラ側で用意してくれればベストなのですが、流石に難しいでしょうし。
最後に、一応念押しというか、お約束というか。
C++ において、メモリ領域の管理は、基本的に手動で行うべきではありません。
動的メモリ領域であれば shared_ptr や unique_ptr を、
スタックやオブジェクト中のメモリ領域であれば boost::optional や boost::variant を、
それぞれ使うべきです。それらのライブラリでは、安全性だけでなく効率も考慮されています。
上に挙げた placement new やデストラクタ呼び出しは、あくまで例であり、
普段 C++ で書くにあたって、そのようなコードを書く必要は一切ありません。
もし万一、何らかの理由が有って、手動でメモリ領域を管理しなければならない場合は、
とにかく慎重に、規格とにらめっこしながらコードを書くようにした方が良いです。
そして、常に「より良い代替」は無いか、を模索するようにしたいですね。