operator delete, operator delete[] - cppreference.com
<tbody> </tbody> <tbody class="t-dcl-rev t-dcl-rev-num "> </tbody><tbody> </tbody> <tbody class="t-dcl-rev t-dcl-rev-num "> </tbody><tbody> </tbody> <tbody class="t-dcl-rev t-dcl-rev-num "> </tbody><tbody> </tbody> <tbody class="t-dcl-rev t-dcl-rev-num "> </tbody><tbody> </tbody> <tbody class="t-dcl-rev t-dcl-rev-num "> </tbody><tbody> </tbody> <tbody class="t-dcl-rev t-dcl-rev-num "> </tbody><tbody> </tbody>
| ヘッダ |
||
| 置き換え可能な通常の解放関数 |
||
| (1) | ||
|
|
(C++11未満) | |
|
|
(C++11以上) | |
| (2) | ||
|
|
(C++11未満) | |
|
|
(C++11以上) | |
|
|
(3) | (C++17以上) |
|
|
(4) | (C++17以上) |
|
|
(5) | (C++14以上) |
|
|
(6) | (C++14以上) |
|
|
(7) | (C++17以上) |
|
|
(8) | (C++17以上) |
| 置き換え可能な配置解放関数 |
||
| (9) | ||
|
|
(C++11未満) | |
|
|
(C++11以上) | |
| (10) | ||
|
|
(C++11未満) | |
|
|
(C++11以上) | |
|
|
(11) | (C++17以上) |
|
|
(12) | (C++17以上) |
| 確保しない配置解放関数 |
||
| (13) | ||
|
|
(C++11未満) | |
|
|
(C++11以上) | |
| (14) | ||
|
|
(C++11未満) | |
|
|
(C++11以上) | |
| ユーザ定義の配置解放関数 |
||
|
|
(15) | |
|
|
(16) | |
| クラス固有の通常の解放関数 |
||
|
|
(17) | |
|
|
(18) | |
|
|
(19) | (C++17以上) |
|
|
(20) | (C++17以上) |
|
|
(21) | |
|
|
(22) | |
|
|
(23) | (C++17以上) |
|
|
(24) | (C++17以上) |
| クラス固有の配置解放関数 |
||
|
|
(25) | |
|
|
(26) | |
| クラス固有の破棄解放関数 |
||
|
|
(27) | (C++20以上) |
|
|
(28) | (C++20以上) |
|
|
(29) | (C++20以上) |
|
|
(30) | (C++20以上) |
対応する operator new によって以前に確保された記憶域を解放します。 これらの解放関数は、動的記憶期間を持つオブジェクトの破棄後 (または構築失敗後)、メモリを解放するために、 delete 式によって、または new 式によって呼ばれます。 これらは通常の関数呼び出し構文を使用して呼ばれる場合もあります。
1) 単一のオブジェクトのために以前に割り当てられた記憶域を解放するために delete 式によって呼ばれます。 ptr がヌルポインタでなく、 operator new(size_t) または operator new(size_t, std::nothrow_t) の標準ライブラリの実装から以前に取得したポインタでもない場合、この関数の標準ライブラリの実装の動作は未定義です。
2) オブジェクトの配列のために以前に割り当てられた記憶域を解放するために delete[] 式によって呼ばれます。 ptr がヌルポインタでなく、 operator new[](size_t) または operator new[](size_t, std::nothrow_t) の標準ライブラリの実装から以前に取得したポインタでもない場合、この関数の標準ライブラリの実装の動作は未定義です。
3,4) (1,2) と同じですが、アライメント要件が __STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に呼ばれます。
5-6) ユーザ定義の置き換えが提供される場合に (1-2) の代わりに呼ばれます。 ただし、不完全型、非クラスの配列、トリビアルに破棄可能なクラス型のオブジェクトを削除するときは、 (1-2) と (5-6) のどちらが呼ばれるか未規定です。 メモリアロケータは渡されたサイズを効率向上のために使用することができます。 標準ライブラリの実装は (1-2) と同一です。
7-8) (5-6) と同じですが、アライメント要件が __STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に呼ばれます。
9) オブジェクトのコンストラクタが例外を投げた場合に、例外を投げない単一オブジェクトの new 式によって呼ばれます。 標準ライブラリの実装は (1) と同じ動作をします。
10) いずれかのオブジェクトのコンストラクタが例外を投げた場合に、例外を投げない配列 new[] 式によって (配列内の構築に成功したすべてのオブジェクトのデストラクタを実行した後で) 呼ばれます。 標準ライブラリの実装は (2) と同じ動作をします。
11,12) (9,10) と同じですが、アライメント要件が __STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に呼ばれます。
13) オブジェクトのコンストラクタが例外を投げた場合に、単一オブジェクトの標準の配置 new 式によって呼ばれます。 この関数の標準ライブラリの実装は何もしません。
14) いずれかのオブジェクトのコンストラクタが例外を投げた場合に配列形式の標準の配置 new 式によって (構築に成功したすべてのオブジェクトのデストラクタを実行した後で) 呼ばれます。 この関数の標準ライブラリの実装は何もしません。
15) 定義されていれば、オブジェクトのコンストラクタが例外を投げた場合に、一致するシグネチャを持つ単一オブジェクトのカスタムな配置 new 式によって呼ばれます。 クラス固有のバージョン (25) が定義されていれば、それが (9) よりも優先的に呼ばれます。 (25) と (15) のいずれもユーザによって提供されない場合、解放関数は呼ばれません。
16) 定義されていれば、いずれかのオブジェクトのコンストラクタが例外を投げた場合に、一致するシグネチャを持つ配列形式のカスタムな配置 new[] 式によって (構築に成功したすべてのオブジェクトのデストラクタを実行した後で) 呼ばれます。 クラス固有のバージョン (26) が定義されていれば、それが (16) よりも優先的に呼ばれます。 (26) と (16) のいずれもユーザによって提供されない場合、解放関数は呼ばれません。
17) 定義されていれば、 T 型のオブジェクトを解放する場合に、単一オブジェクトの通常の delete 式によって呼ばれます。
18) 定義されていれば、 T 型のオブジェクトの配列を解放する場合に、配列の通常の delete[] 式によって呼ばれます。
19,20) 定義されていれば、アライメント要件が __STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、 (17,18) よりも優先的に呼ばれます。
21) 定義されていれば、かつ (17) が定義されていなければ、 T 型のオブジェクトを解放する場合に、単一オブジェクトの通常の delete 式によって呼ばれます。
22) 定義されていれば、かつ (18) が定義されていなければ、 T 型のオブジェクトの配列を解放する場合に、配列の通常の delete[] 式によって呼ばれます。
23,24) 定義されていれば、かつ (19,20) が定義されていなければ、アライメント要件が __STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、アロケータ非対応のメンバよりも優先的に呼ばれます。
25) 定義されていれば、オブジェクトのコンストラクタが例外を投げた場合に、一致するシグネチャを持つ単一オブジェクトのカスタムな配置 new によって呼ばれます。 この関数が提供されず、一致する (15) も提供されない場合、解放関数は呼ばれません。
26) 定義されていれば、いずれかのオブジェクトのコンストラクタが例外を投げた場合に、一致するシグネチャを持つ配列形式のカスタムな配置 new[] によって (構築に成功したすべてのオブジェクトのデストラクタを実行した後で) 呼ばれます。 この関数が提供されず、一致する (16) も提供されない場合、解放関数は呼ばれません。
27-30) 定義されていれば、 delete 式は operator delete の呼び出しを行う前の *p のデストラクタを実行しません。 代わりに、このユーザ定義の operator delete が p->~T(); のようにデストラクタを直接呼ぶ責任を持ちます。
|
通常の (配置でない) 解放関数のアライメント対応とアライメント非対応のオーバーロード解決のルールの正確な詳細は delete 式を参照してください。 |
(C++17以上) |
すべての場合において、 ptr がヌルポインタの場合、標準ライブラリの解放関数は何もしません。 標準ライブラリの解放関数に渡されるポインタが対応する標準ライブラリの確保関数から取得したものでない場合、動作は未定義です。
標準ライブラリの解放関数が戻った後、その解放された記憶域のいずれかの部分を参照するすべてのポインタは無効になります。
|
この方法で無効になったポインタのあらゆる使用は、たとえそのポインタ値を別の変数にコピーしていても、未定義動作です。 |
(C++14未満) |
|
この方法で無効になったポインタを通した間接参照、およびそれを解放関数に渡すこと (二重削除) は、未定義動作です。 その他のあらゆる使用は処理系定義です。 |
(C++14以上) |
引数
| ptr | - | 解放するメモリブロックを指すポインタ、またはヌルポインタ |
| sz | - | 一致する確保関数に渡されたサイズ |
| place | - | 一致する配置 new で配置引数として使用されたポインタ |
| tag | - | 例外を投げない operator new によって使用されたタグに一致するオーバーロードの曖昧性解決のタグ |
| al | - | 確保されたオブジェクトまたは配列要素のアライメント |
| args | - | 配置確保関数と一致する適切な引数 (std::size_t および std::align_val_t を含む場合があります) |
戻り値
(なし)
例外
|
15-30) ユーザ定義解放関数は例外を投げても構いません。 |
(C++11未満) |
|
15-30) すべての解放関数は、宣言で他の値が指定されない限り、 |
(C++11以上) |
グローバルな置き換え
置き換え可能な解放関数 (1-10) は <new> ヘッダがインクルードされていなくても各翻訳単位で暗黙に定義されます。 これらの関数は置き換え可能です。 プログラム内の任意のソースファイル内の任意の場所で定義された同じシグネチャのユーザ提供の非メンバ関数は、対応する暗黙のバージョンをプログラム全体に対して置き換えます。 その宣言が可視である必要はありません。
プログラム内で2つ以上の置き換えが提供された場合、動作は未定義です。 置き換えが inline 指定子付きで定義された場合、動作は未定義です。 置き換えがグローバル名前空間以外の名前空間で定義された場合、動作は未定義です。 置き換えがグローバルスコープで static な非メンバ関数として定義された場合、動作は未定義です。
|
例外を投げないバージョン (9,10) の標準ライブラリの実装は対応する例外を投げるバージョン (1,2) を直接呼びます。 サイズ対応解放関数 (5-8) の標準ライブラリの実装は対応するサイズ非対応の確保関数 (1-4) を直接呼びます。 サイズ非対応の例外を投げる配列形式 (2,4) の標準ライブラリの実装は対応する単一オブジェクト形式 (1,3) を直接呼びます。 そのため、すべての解放を処理するためには、例外を投げる単一オブジェクトの解放関数 (1,3) を置き換えれば十分です。 |
(C++11以上) |
#include <cstdio> #include <cstdlib> // 確保/解放関数の最低限の置き換え void* operator new(std::size_t sz) { std::printf("global op new called, size = %zu\n",sz); return std::malloc(sz); } void operator delete(void* ptr) noexcept { std::puts("global op delete called"); std::free(ptr); } int main() { int* p1 = new int; delete p1; int* p2 = new int[10]; // C++11 では置き換えを呼ぶことが保証されます。 delete[] p2; }
出力例:
global op new called, size = 4 global op delete called global op new called, size = 40 global op delete called
追加のユーザ定義引数を持つ operator delete および operator delete[] のオーバーロード (「配置形式」、 (15,16)) は、通常通りグローバルスコープで宣言することができ、確保中のオブジェクトのコンストラクタが例外を投げた場合、一致する配置形式の new 式によって呼ばれます。
標準ライブラリの配置形式の operator delete (13,14) は、置き換えることはできず、配置 new 式が ::new 構文を使用しない場合に、一致するシグネチャ void T::operator delete(void*, void*) または void T::operator delete[](void*, void*) を持つクラス固有の配置 delete (25,26) を提供することによってのみ、カスタマイズできます。
クラス固有のオーバーロード
解放関数 (17-24) はクラスの static メンバ関数として定義できます。 これらの解放関数は、提供されていれば、そのクラスのオブジェクト (17,19,21) および配列 (18,20,22) を削除するときに delete 式によって呼ばれます。 ただしその delete 式がクラススコープの探索をバイパスする ::delete の形式を使用している場合を除きます。 キーワード static はこれらの関数の宣言に対しては任意であり、使用されようがされなかろうが、解放関数は必ず static メンバ関数です。
delete 式は適切な解放関数の名前をまずクラススコープから (配列形式はその配列の要素クラスのスコープから) 探し、通常通り、メンバが見つからない場合は、グローバルスコープから探します。 名前探索のルールにより、クラススコープで宣言されたいずれかの解放関数は、すべてのグローバル解放関数を隠蔽することに注意してください。
削除されるオブジェクトの静的な型がその動的な型と異なる場合 (基底へのポインタを通して多相オブジェクトを削除するときなど)、そしてその静的な型のデストラクタが virtual な場合、単一オブジェクト形式の delete はその仮想デストラクタの最終オーバーライダーの定義地点から解放関数の名前の探索を開始します。 実行時にどの解放関数が実行されるかにかかわらず、コンパイルするためには operator delete の静的に可視なバージョンがアクセス可能でなければなりません。 それ以外の場合では、基底へのポインタを通して配列を削除するとき、または非仮想デストラクタを持つ基底へのポインタを通してアクセスするとき、動作は未定義です。
単一引数のオーバーロード (17,18) は提供されないけれども第2引数として std::size_t を取るサイズ対応のオーバーロード (21,22) が提供される場合、通常の解放のためにサイズ対応形式が呼ばれ、 C++ のランタイムにより解放中のオブジェクトのサイズが第2引数として渡されます。 両方の形式が定義されている場合、サイズ非対応のバージョンが呼ばれます。
#include <iostream> // サイズ付きのクラス固有の解放関数 struct X { static void operator delete(void* ptr, std::size_t sz) { std::cout << "custom delete for size " << sz << '\n'; ::operator delete(ptr); } static void operator delete[](void* ptr, std::size_t sz) { std::cout << "custom delete for size " << sz << '\n'; ::operator delete(ptr); } }; int main() { X* p1 = new X; delete p1; X* p2 = new X[10]; delete[] p2; }
出力例:
custom delete for size 1 custom delete for size 18
追加のユーザ定義引数を持つ operator delete および operator delete[] のオーバーロード (「配置形式」、(25,26)) もクラスメンバとして定義できます。 失敗した配置 new 式が呼ぶための対応する配置 delete 関数を探するときは、グローバルスコープを調べる前にクラススコープから探索を開始し、その配置 new に一致するシグネチャを持つ関数を探します。
#include <stdexcept> #include <iostream> struct X { X() { throw std::runtime_error(""); } // カスタム配置 new static void* operator new(std::size_t sz, bool b) { std::cout << "custom placement new called, b = " << b << '\n'; return ::operator new(sz); } // カスタム配置 delete static void operator delete(void* ptr, bool b) { std::cout << "custom placement delete called, b = " << b << '\n'; ::operator delete(ptr); } }; int main() { try { X* p1 = new (true) X; } catch(const std::exception&) { } }
出力:
custom placement new called, b = 1 custom placement delete called, b = 1
クラスレベルの operator delete がテンプレート関数の場合、その戻り値型は void、第1引数は void* でなければならず、2つ以上の引数を取らなければなりません。 別の言い方をすると、配置形式のみがテンプレート化できます。 テンプレート operator delete の特殊化はテンプレートの実引数推定で選択されます。
ノート
多相クラスに対するクラス固有の T::operator delete の呼び出しは、 static メンバ関数が動的ディスパッチを通して呼ばれる唯一のケースです。
|
以下の関数はスレッドセーフであることが要求されます。
記憶域の特定の単位を確保または解放するこれらの関数の呼び出しは単一の全順序で発生し、そのような解放の呼び出しそれぞれはこの順序における次の確保 (もしあれば) に対して先行発生します。 |
(C++11以上) |
欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
| DR | 適用先 | 発行時の動作 | 正しい動作 |
|---|---|---|---|
| LWG 2458 | C++14 | overloads taking (void*,size_t,const nothrow_t&) were specified, but could never be called | spurious overloads removed |