operator new, operator new[]
| ヘッダ <new> で定義
|
||
| 置き換え可能な確保関数 [[nodiscard]] (C++20以上) |
||
void* operator new ( std::size_t count ); |
(1) | |
void* operator new[]( std::size_t count ); |
(2) | |
void* operator new ( std::size_t count, std::align_val_t al); |
(3) | (C++17以上) |
void* operator new[]( std::size_t count, std::align_val_t al); |
(4) | (C++17以上) |
| 置き換え可能な例外を投げない確保関数 [[nodiscard]] (C++20以上) |
||
void* operator new ( std::size_t count, const std::nothrow_t& tag); |
(5) | |
void* operator new[]( std::size_t count, const std::nothrow_t& tag); |
(6) | |
void* operator new ( std::size_t count, std::align_val_t al, const std::nothrow_t&); |
(7) | (C++17以上) |
void* operator new[]( std::size_t count, std::align_val_t al, const std::nothrow_t&); |
(8) | (C++17以上) |
| 確保しない配置確保関数 [[nodiscard]] (C++20以上) |
||
void* operator new ( std::size_t count, void* ptr ); |
(9) | |
void* operator new[]( std::size_t count, void* ptr ); |
(10) | |
| ユーザ定義の配置確保関数 |
||
void* operator new ( std::size_t count, user-defined-args... ); |
(11) | |
void* operator new[]( std::size_t count, user-defined-args... ); |
(12) | |
void* operator new ( std::size_t count, std::align_val_t al, user-defined-args... ); |
(13) | (C++17以上) |
void* operator new[]( std::size_t count, std::align_val_t al, user-defined-args... ); |
(14) | (C++17以上) |
| クラス固有の確保関数 |
||
void* T::operator new ( std::size_t count ); |
(15) | |
void* T::operator new[]( std::size_t count ); |
(16) | |
void* T::operator new ( std::size_t count, std::align_val_t al ); |
(17) | (C++17以上) |
void* T::operator new[]( std::size_t count, std::align_val_t al ); |
(18) | (C++17以上) |
| クラス固有の配置確保関数 |
||
void* T::operator new ( std::size_t count, user-defined-args... ); |
(19) | |
void* T::operator new[]( std::size_t count, user-defined-args... ); |
(20) | |
void* T::operator new ( std::size_t count, std::align_val_t al, user-defined-args... ); |
(21) | (C++17以上) |
void* T::operator new[]( std::size_t count, std::align_val_t al, user-defined-args... ); |
(22) | (C++17以上) |
要求されたバイト数を確保します。 これらの確保関数は、初期化される新しいオブジェクトのためのメモリを確保するために、 new 式 によって呼ばれます。 これらは普通の関数呼び出し構文を使用して呼ぶこともできます。
count バイトを確保します。 失敗した場合、標準ライブラリの実装は std::get_new_handler によって返される関数ポインタを呼び、 new handler が戻らないかヌルポインタになるまで確保の試みを繰り返します。 new handler がヌルポインタの場合は std::bad_alloc を投げます。 この関数は要求されたサイズのオブジェクトを指すための適切にアライメントされたポインタを返すことが要求されます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超えるアライメント要件を持つ単一のオブジェクトのために要求される記憶域を確保するために、非配列の new 式によって呼ばれます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超えるアライメント要件を持つオブジェクトの配列のために要求される記憶域を確保するために、配列形式の new[] 式によって呼ばれます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、例外を投げない非配列の new 式によって呼ばれます。 標準ライブラリの実装はバージョン (3) を呼び、失敗時は例外を伝播する代わりにヌルポインタを返します。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、例外を投げない配列形式の new[] 式によって呼ばれます。 標準ライブラリの実装はバージョン (4) を呼び、失敗時は例外を伝播する代わりにヌルポインタを返します。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、一致するシグネチャを持つ単一オブジェクトのカスタムな配置 new 式によって呼ばれます。 クラス固有のバージョン ((15) または (17)) が定義されていれば、それが代わりに呼ばれます。 クラス固有のバージョンもグローバルなアライメント対応の配置形式 (このバージョン) も提供されていなければ、代わりにアライメント非対応の配置形式 (11) が探索されます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、一致するシグネチャを持つ配列形式のカスタムな配置 new 式によって呼ばれます。 クラス固有のバージョン ((16) または (18)) が定義されていれば、それが代わりに呼ばれます。 クラス固有のバージョンもグローバルなアライメント対応の配置形式 (このバージョン) も提供されていなければ、代わりにアライメント非対応の配置形式 (12) が探索されます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、単一オブジェクトの通常の new 式によって呼ばれます。 このオーバーロードが提供されないけれどもアライメント非対応の形式 (15) が提供されている場合は、代わりにアライメント非対応のオーバーロードが呼ばれます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、配列形式の通常の new[] 式によって呼ばれます。 このオーバーロードが提供されないけれどもアライメント非対応の形式 (16) が提供されている場合は、代わりにアライメント非対応のオーバーロードが呼ばれます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、単一オブジェクトのカスタムな配置 new 式によって呼ばれます。 このオーバーロードが提供されないけれどもアライメント非対応の形式 (19) が提供されている場合は、代わりにアライメント非対応のオーバーロードが呼ばれます。__STDCPP_DEFAULT_NEW_ALIGNMENT__ を超える場合に、配列形式のカスタムな配置 new[] 式によって呼ばれます。 このオーバーロードが提供されないけれどもアライメント非対応の形式 (20) が提供されている場合は、代わりにアライメント非対応のオーバーロードが呼ばれます。引数
| count | - | 確保するバイト数 |
| ptr | - | オブジェクトを初期化するためのメモリ領域を指すポインタ |
| tag | - | 例外を投げないオーバーロードを選択するために使用される曖昧性解消タグ |
| al | - | 使用するアライメント。 有効なアライメントの値でない場合、動作は未定義です |
戻り値
size バイトの適切にアライメントされたメモリを指す非ヌルなポインタ。size バイトの適切にアライメントされたメモリを指す非ヌルなポインタ、または確保失敗時はヌルポインタ。ptr。例外
|
(なし) |
(C++11未満) |
|
noexcept 指定:
noexcept |
(C++11以上) |
グローバルな置き換え
バージョン (1-4) は <new> ヘッダがインクルードされていなくても各翻訳単位で暗黙に定義されます。
バージョン (1-8) は置き換え可能です。 プログラム内の任意のソースファイル内の任意の場所で定義された同じシグネチャのユーザ提供の非メンバ関数は、デフォルトのバージョンを置き換えます。 その宣言が可視である必要はありません。
いずれかの置き換え可能な関数に対してプログラム内で2つ以上の置き換えが提供された場合、動作は未定義です。 置き換えが inline 指定子付きで定義された場合、動作は未定義です。 置き換えがグローバル名前空間以外の名前空間で定義された場合、動作は未定義です。 置き換えがグローバルスコープで static な非メンバ関数として定義された場合、動作は未定義です。
|
例外を投げないバージョン (5-8) の標準ライブラリの実装は、対応する例外を投げるバージョン (1-4) を直接呼びます。 例外を投げる配列バージョン (2,4) の標準ライブラリの実装は、対応する単一オブジェクトバージョン (1,3) を直接呼びます。 そのため、すべての確保を処理するためには、例外を投げる単一オブジェクトの確保関数を置き換えれば十分です。 |
(C++11以上) |
#include <cstdio>
#include <cstdlib>
#include <new>
// 最低限の関数の置き換え
void* operator new(std::size_t sz) {
std::printf("global op new called, size = %zu\n", sz);
void *ptr = std::malloc(sz);
if (ptr)
return ptr;
else
throw std::bad_alloc{};
}
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 new および operator new[] のオーバーロード (11-14) (「配置形式」) は通常通りグローバルスコープで宣言でき、一致する配置形式の new 式 によって呼ばれます。
標準ライブラリの確保しない配置形式の operator new (9-10) は、置き換えることはできず、配置 new 式が ::new 構文を使用しない場合に、一致するシグネチャ (void* T::operator new(size_t, void*) または void* T::operator new[](size_t, void*)) を持つクラス固有の配置 new (19,20) を提供することによってのみ、カスタマイズできます。
|
配置形式 |
(C++14以上) |
クラス固有のオーバーロード
単一オブジェクトと配列の確保関数はどちらも、クラスのパブリックな static メンバとして定義できます (バージョン {v|15-18}})。 定義されていれば、そのクラスの単一オブジェクトまたは配列のためのメモリを確保するために、 new 式によってこれらの確保関数が呼ばれます。 ただしその new 式がクラススコープの探索をバイパスする ::new の形式を使用している場合を除きます。 キーワード static はこれらの関数に対しては任意であり、使用されようがされなかろうが、確保関数は static メンバ関数です。
new 式は適切な確保関数の名前をまずクラススコープから、その後グローバルスコープから探します。 名前探索のルールにより、クラススコープで宣言されたいずれかの確保関数は、そのクラスのオブジェクトの確保を試みる new 式に対して、すべてのグローバル関数を隠蔽することに注意してください。
|
|
(C++17以上) |
#include <iostream>
// クラス固有の確保関数
struct X {
static void* operator new(std::size_t sz)
{
std::cout << "custom new for size " << sz << '\n';
return ::operator new(sz);
}
static void* operator new[](std::size_t sz)
{
std::cout << "custom new for size " << sz << '\n';
return ::operator new(sz);
}
};
int main() {
X* p1 = new X;
delete p1;
X* p2 = new X[10];
delete[] p2;
}
出力例:
custom new for size 1
custom new for size 10
追加のユーザ定義の引数を持つ operator new および operator new[] のオーバーロード (「配置形式」) は、クラスメンバとして定義することもできます (19-22)。 一致するシグネチャを持つ配置 new 式が呼ぶべき対応する確保関数を探すときは、グローバルスコープを調べる前にクラススコープから開始し、クラス固有の配置 new が提供されていれば、それが呼ばれます。
|
|
(C++17以上) |
#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 new がテンプレート関数の場合、その戻り値型は void*、第1引数は std::size_t でなければならず、2つ以上の引数を取らなければなりません。 別の言い方をすると、配置形式のみがテンプレート化できます。
ノート
確保しない配置 new (9,10) を置き換えることはできませんが、上記の通り、クラススコープで同じシグネチャの関数を定義することはできます。 また、第2引数として void でないポインタを取る配置 new っぽいグローバルなオーバーロードも可能であるため、真の配置 new が呼ばれることを保証したいコード (std::allocator::construct など) は、 ::new を使用しなければならず、また、ポインタを void* にキャストしなければなりません。
|
以下の関数はスレッドセーフであることが要求されます。
記憶域の特定の単位を確保または解放するこれらの関数の呼び出しは単一の全順序で発生し、そのような解放の呼び出しそれぞれはこの順序における次の確保 (もしあれば) に対して先行発生します。 |
(C++11以上) |
operator new のライブラリバージョンが std::malloc または std::aligned_alloc (C++17以上) の呼び出しを行うかどうかは未規定です。
例
operator new のカスタム配置形式は任意の目的に使用できます。 例えば確保した配列を塗りつぶすとか。
#include <iostream>
#include <algorithm>
void* operator new[](std::size_t sz, char c)
{
void* p = operator new[](sz);
std::fill_n(reinterpret_cast<char*>(p), sz, c);
return p;
}
int main()
{
char* p = new('*') char[6];
p[5] = '\0';
std::cout << p << '\n';
delete[] p;
}
出力:
*****
関連項目
| 解放関数 (関数) | |
(C++11) |
現在の new ハンドラを取得します (関数) |
| new ハンドラを登録します (関数) | |
(C++17で非推奨)(C++20で削除) |
未初期化記憶域を取得します (関数テンプレート) |
| メモリを確保します (関数) | |
(C++17) |
アラインされたメモリを確保します (関数) |
参考文献
- C++11 standard (ISO/IEC 14882:2011):
- 18.6 Dynamic memory management [support.dynamic]