new
演算子と delete
演算子
C++ では、new
演算子と delete
演算子を使用して、オブジェクトの動的割り当てと割り当て解除をサポートしています。 これらの演算子は、フリー ストア (ヒープとも呼ばれます) と呼ばれるプールのオブジェクトにメモリを割り当てます。 new
演算子は特殊な関数 operator new
を呼び出し、delete
演算子は特殊な関数 operator delete
を呼び出します。
C ランタイム ライブラリと C++ 標準ライブラリのライブラリ ファイルの一覧については、「CRT ライブラリの機能」をご覧ください。
new
演算子
コンパイラは、次のようなステートメントを関数 operator new
の呼び出しに変換します。
char *pch = new char[BUFFER_SIZE];
要求のサイズがゼロバイトのストレージである場合、operator new
は別のオブジェクトへのポインターを返します。 つまり、operator new
を繰り返し呼び出して、異なるポインターを返します。
割り当て要求のメモリが不足している場合、operator new
は std::bad_alloc
例外をスローします。 または、配置フォームを使用した場合、またはスローoperator new
しないサポートでリンクしている場合に返されますnullptr
new(std::nothrow)
。 詳細については、「割り当てエラーの動作」を参照してください。
operator new
関数の 2 つのスコープの説明を次の表に示します。
operator new
関数のスコープ
Operator | Scope |
---|---|
::operator new |
グローバル |
class-name::operator new |
クラス |
最初の operator new
引数は型 size_t
である必要があり、戻り値の型は常に void*
です。
グローバルな operator new
関数は、new
演算子が、組み込み型のオブジェクト、ユーザー定義の operator new
関数を含まないクラス型のオブジェクト、および任意の型の配列の割り当てに使用されるときに呼び出されます。 operator new
が定義されたクラス型のオブジェクトを割り当てるために new
演算子が使用されると、そのクラスの operator new
が呼び出されます。
クラスに定義された operator new
関数は、そのクラス型のオブジェクトのグローバルな operator new
関数を隠す静的メンバー関数です (したがって仮想関数にはできません)。 new
を使用してメモリを割り当て、指定した値に設定する場合を考えます。
#include <malloc.h>
#include <memory.h>
class Blanks
{
public:
Blanks(){}
void *operator new( size_t stAllocateBlock, char chInit );
};
void *Blanks::operator new( size_t stAllocateBlock, char chInit )
{
void *pvTemp = malloc( stAllocateBlock );
if( pvTemp != 0 )
memset( pvTemp, chInit, stAllocateBlock );
return pvTemp;
}
// For discrete objects of type Blanks, the global operator new function
// is hidden. Therefore, the following code allocates an object of type
// Blanks and initializes it to 0xa5
int main()
{
Blanks *a5 = new(0xa5) Blanks;
return a5 != 0;
}
new
のかっこ内で指定された引数は、chInit
引数として Blanks::operator new
に渡されます。 ただし、グローバルな operator new
関数は隠されているため、次のようなコードではエラーが生成されます。
Blanks *SomeBlanks = new Blanks;
コンパイラは、クラス宣言でメンバー配列 new
演算子と delete
演算子をサポートしています。 次に例を示します。
class MyClass
{
public:
void * operator new[] (size_t)
{
return 0;
}
void operator delete[] (void*)
{
}
};
int main()
{
MyClass *pMyClass = new MyClass[5];
delete [] pMyClass;
}
割り当てエラーの動作
C++ 標準ライブラリの関数は new
、C++ 98 以降の C++ 標準で指定された動作をサポートしています。 割り当て要求のメモリが不足している場合は、 operator new
例外を std::bad_alloc
スローします。
以前の C++ コードでは、割り当てが失敗した場合に null ポインターが返されました。 スローしないバージョン new
のコードがある場合は、プログラム nothrownew.obj
を . このファイルは nothrownew.obj
、グローバル operator new
を、割り当てが失敗した場合に nullptr
返されるバージョンに置き換えます。 operator new
がスローされ std::bad_alloc
なくなりました。 リンカー オプション ファイルとその他の詳細についてはnothrownew.obj
、「リンク オプション」を参照してください。
グローバルoperator new
からの例外にチェックするコードと、同じアプリケーション内の null ポインターにチェックするコードを混在させることはありません。 ただし、動作が異なるクラスローカル operator new
を作成することもできます。 この可能性は、コンパイラが既定で防御的に動作し、null ポインターの戻り値のチェックを呼び出しにnew
含める必要があります。 これらのコンパイラのチェックを最適化する方法の詳細については、次を参照してください/Zc:throwingnew
。
メモリ不足の処理
式からの new
割り当ての失敗をテストする方法は、標準の例外メカニズムを使用するか、戻り値を nullptr
使用するかによって異なります。 標準 C++ では、アロケーターがスローされるか std::bad_alloc
、またはから std::bad_alloc
派生したクラスをスローすることを想定しています。 このサンプルに示すように、このような例外を処理できます。
#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
try {
int *pI = new int[BIG_NUMBER];
}
catch (bad_alloc& ex) {
cout << "Caught bad_alloc: " << ex.what() << endl;
return -1;
}
}
次の形式を nothrow
使用すると、次の new
サンプルに示すように、割り当てエラーをテストできます。
#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
int *pI = new(nothrow) int[BIG_NUMBER];
if ( pI == nullptr ) {
cout << "Insufficient memory" << endl;
return -1;
}
}
次に示すように、ファイルを使用 nothrownew.obj
してグローバル operator new
を置き換えたときに、メモリ割り当ての失敗をテストできます。
#include <iostream>
#include <new>
using namespace std;
#define BIG_NUMBER 10000000000LL
int main() {
int *pI = new int[BIG_NUMBER];
if ( !pI ) {
cout << "Insufficient memory" << endl;
return -1;
}
}
失敗したメモリ割り当て要求のハンドラーを指定できます。 このようなエラーを処理するカスタム回復ルーチンを記述できます。 たとえば、予約済みメモリを解放してから、割り当てを再実行できます。 詳細については、_set_new_handler
を参照してください。
delete
演算子
new
演算子を使用して動的に割り当てられたメモリは、delete
演算子を使用して解放できます。 delete 演算子は operator delete
関数を呼び出し、この関数がメモリを解放して使用可能なプールに戻します。 delete
演算子を使用すると、クラス デストラクターも呼び出されます (存在する場合)。
グローバルとクラス スコープの operator delete
関数があります。 特定のクラスに対して定義できる operator delete
関数は、1 つだけです。定義されている場合、グローバルの operator delete
関数は隠されます。 グローバルの operator delete
関数は、常に任意の型の配列に対して呼び出されます。
グローバルの operator delete
関数。 グローバルの operator delete
関数とクラスメンバー operator delete
関数には、次の 2 つの形式があります。
void operator delete( void * );
void operator delete( void *, size_t );
特定のクラスに存在できるのは、上記の 2 つの形式のうち 1 つだけです。 最初の形式は、型 void *
の 1 つの引数を受け取ります。この引数には、割り当てを解除するオブジェクトへのポインターが含まれています。 2 番目の形式であるサイズ割り当て解除は 2 つの引数を取ります。最初の引数は割り当て解除するメモリ ブロックへのポインターで、2 番目は割り当て解除するバイト数です。 両方の形式の戻り値の型は void
です (operator delete
は値を返すことができません)。
2 番目の形式の目的は、削除するオブジェクトの正しいサイズ カテゴリの検索を高速化することです。 多くの場合、この情報は割り当て自体の近くに格納されず、キャッシュされない可能性が高いです。 2 番目の形式は、基底クラスの operator delete
関数を派生クラスのオブジェクトの削除に使用するときに便利です。
operator delete
関数は静的であるため、仮想にすることはできません。 operator delete
関数は、「メンバー アクセス コントロール」で説明されているように、アクセス制御に従います。
次の例は、メモリの割り当てと解放を記録するように設計された、ユーザー定義の operator new
関数と operator delete
関数を示しています。
#include <iostream>
using namespace std;
int fLogMemory = 0; // Perform logging (0=no; nonzero=yes)?
int cBlocksAllocated = 0; // Count of blocks allocated.
// User-defined operator new.
void *operator new( size_t stAllocateBlock ) {
static int fInOpNew = 0; // Guard flag.
if ( fLogMemory && !fInOpNew ) {
fInOpNew = 1;
clog << "Memory block " << ++cBlocksAllocated
<< " allocated for " << stAllocateBlock
<< " bytes\n";
fInOpNew = 0;
}
return malloc( stAllocateBlock );
}
// User-defined operator delete.
void operator delete( void *pvMem ) {
static int fInOpDelete = 0; // Guard flag.
if ( fLogMemory && !fInOpDelete ) {
fInOpDelete = 1;
clog << "Memory block " << cBlocksAllocated--
<< " deallocated\n";
fInOpDelete = 0;
}
free( pvMem );
}
int main( int argc, char *argv[] ) {
fLogMemory = 1; // Turn logging on
if( argc > 1 )
for( int i = 0; i < atoi( argv[1] ); ++i ) {
char *pMem = new char[10];
delete[] pMem;
}
fLogMemory = 0; // Turn logging off.
return cBlocksAllocated;
}
上記のコードを "メモリ リーク" の検出に使うことができます。メモリ リークとは、フリー ストアに割り当てられ、解放されていないメモリを指します。 リークを検出するために、グローバルな new
演算子と delete
演算子がメモリの割り当てと解放をカウントするように再定義されています。
コンパイラは、クラス宣言でメンバー配列 new
演算子と delete
演算子をサポートしています。 次に例を示します。
// spec1_the_operator_delete_function2.cpp
// compile with: /c
class X {
public:
void * operator new[] (size_t) {
return 0;
}
void operator delete[] (void*) {}
};
void f() {
X *pX = new X[5];
delete [] pX;
}
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示