C++ 標準では、配列の割り当てあるいは割り当て解除を行うときに呼び出される operator new と operator delete の新しい形式が追加されています。従来は、これらの operator 関数は 1 つの形式しかありませんでした。また、配列の割り当てでは、大域形式の operator new と operator delete が使用され、クラス固有の形式は使用されませんでした。新しい形式を使用するには、ABI の変更が必要になるため、C++ 4.2 コンパイラでは、新しい形式はサポートされていません。
次の関数に加えて、
void* operator new(size_t);
void operator delete(void*);
C++ 標準では、以下の関数が追加されています。
void* operator new[](size_t);
void operator delete[](void*);
新旧いずれの場合も、実行時ライブラリにある形式とは別の形式を記述することができます。このように 2 つの形式が用意されているのは、配列と個々のオブジェクトに対して異なるメモリープールを使用できるようにするためと、配列に対してクラスが独自の形式の operator new を提供できるようにするためです。
新旧どちらの規則でも、new T と記述すると (T は特定の型)、operator new(size_t) 関数が呼び出されます。ただし、新しい規則で new T[n] と記述すると、operator new[](size_t) 関数が呼び出されます。
同様にどちらの規則でも delete p と記述すると、operator delete(void*) が呼び出されます。ただし、新しい規則で delete [] p; と記述すると、operator delete[](void*) が呼び出されます。
これらの関数について、クラス固有の配列形式を記述することもできます。
古い規則では、割り当てに失敗すると、どの形式の operator new でも NULL ポインタ を返します。新しい規則では、割り当てに失敗すると、通常の形式の operator new では例外を送出し、値は返しません。このほか、例外を送出する代わりにゼロを返す特殊な形式の operator new もあります。どの形式の operator new および operator delete にも、「例外指定」があります。次は、標準ヘッダーの <new> にある宣言です。
namespace std { class bad_alloc; struct nothrow_t {}; extern const nothrow_t nothrow; } // 単一オブジェクト形式 void* operator new(size_t size) throw(std::bad_alloc); void* operator new(size_t size, const std::nothrow_t&) throw(); void operator delete(void* ptr) throw(); void operator delete(void* ptr, const std::nothrow_t&) throw(); // 配列形式 void* operator new[](size_t size) throw(std::bad_alloc); void* operator new[](size_t size, const std::nothrow_t&) throw(); void operator delete[](void* ptr) throw(); void operator delete[](void* ptr, const std::nothrow_t&) throw();
次の例に示すような安全対策のためのコードは、新しい規則では意図したとおりには動作しません。割り当てに失敗すると、new 式から自動的に呼び出される operator new によって例外が送出され、ゼロを判定する検査は行われません。
T* p = new T; if( p == 0 ) { // 新しい規則ではエラー ... // 割り当て失敗の処理 } ... // pを使用する
このような場合には、次の 2 つの方法で解決できます。
以下のように、コードを記述し直して例外を捕獲できるようにする。
T* p = 0; try { p = new T; } catch( std::bad_alloc& ) { ... // 割り当て失敗の処理 } ... // pを使用する
コード中で例外を使用したくない場合は、2 番目の形式を使用してください。コード中で例外を使用するときは、最初の形式をお勧めします。
operator new が成功するかどうかを確認していない場合は、既存のコードを変更せずにそのまま使用してもかまいません。不正なメモリー参照が発生する箇所まで処理が進むことはなく、プログラムは割り当てに失敗した時点で異常終了します。
別の形式の operator new と operator delete を使用している場合、その関数は、例外の指定を含めて前述の例と同じ識別形式である必要があります。また、実装されている意味も同じである必要があります。通常の形式の operator new では、失敗時に bad_alloc 例外を送出する必要があります。これに対して nothrow 形式では、失敗時に例外を送出せずに、ゼロを返す必要があります。operator delete では、どの形式についても、例外を送出してはいけません。標準ライブラリのコードでは、大域的な operator new と operator delete が使用されており、コードが正しく実行されるかどうかは、その動作に依存します。他社のライブラリについても、同様の依存関係が存在する可能性があります。
C++ 5.0 の実行時ライブラリの大域形式の operator new[]() は、C++ 標準で規定されているように、単一オブジェクト形式の operator new() を呼び出すだけです。C++ 5.0 の標準ライブラリの大域形式の operator new() を置き換える場合、大域形式の operator new[]() を置き換える必要はありません。
C++ 標準では、あらかじめ定義されている、以下の「配置」形式の operator new の置き換えを禁止しています。
void* operator new(std::size_t, void*) throw();
void* operator new[](std::size_t, void*) throw();
上記の置き換えは、4.2 コンパイラでは許可されますが、C++ 5.0 標準モードでは置換できません。4.2コンパイラでは、別のパラメータリストを使用して独自の置き換えを記述することもできます。
互換モードの場合は、通常どおり <new.h> をインクルードしてください。標準モードでは、代わりに <new> (.h なし) をインクルードしてください。簡単に移行できるよう、標準モードでは、ヘッダーの <new.h> を使用すると、名前空間 std の名前を大域の名前空間が使用できます。このヘッダーには、例外の古い名前を新しい名前に対応させる typedef も用意されています。