new と delete (1) -- placement new
オブジェクトを動的に確保したり解放したりする場合は
new / delete 演算子を使うわけだが、
デフォルトの new / delete 演算子を呼ぶと、
結局のところ malloc とか free に落ちることになるので、
ヒープ領域の管理コストがかかることになる。
たまに比較的大きなオブジェクトを生成するのに new
を呼ぶ分にはそのコストも無視できるだろうが、サイズの小さい (たとえば、4バイトとか)
オブジェクトを何百万回も生成 / 破棄するようなプログラムだと、
その時間的空間的な管理コストは無視できないほど大きくなる。
そのような場合に使われるのが、placement new (配置 new)
というテクニックである。
次のようにふつうに new 演算子を使うと、
X *x = new X;
operator new( size_t ) が呼び出され、
という流れになる。
で、実のところ、この operator new はオーバーロードすることが可能だ。
たとえば、
class X {
public:
static void *operator new( size_t size, void *buf ) { return buf; }
};
を定義しておけば、次のような呼び方が可能になる。
char buffer[ sizeof(X) ]; X *x = new( buffer ) X;
こうすると、あらかじめ確保しておいたメモリ領域を使って、 そこにオブジェクトを構築することができる。
placement new の構文をより一般的に書くと次のようになる。
operator newは第1引数として型 size_t を取るが、 それ以降の引数の型および数は任意に定義できる- 第1引数には、
new演算子のオペランドたる型の sizeof 値が渡されるnew演算子に( )で囲まれた引数を付加すると、 第2引数以降に対応した型と数を持つoperator newが呼び出される (対応するoperator newが宣言されていなければコンパイルエラー)
たとえば、T、U、V が型であるとして、
class Y {
public:
static void *operator new( size_t size, T& t, U& u, V& v );
};
のような宣言があれば、
T t; U u; V v; Y *y = new( t, u, v ) Y;
といった呼び出しができるというわけである。この形でよく使われるのは、 自前でメモリアロケータのクラス (MyAllocator など) を用意しておいて、
static void *operator new( size_t size, MyAllocator& allocator ) {
return allocator.allocate( size );
}
などとするやり方だ。
さて、このような operator new を宣言した場合は、
それと対となる operator delete も宣言してやらねばならないのだが、
詳しくは次回。
初出: 2002年5月29日