web-dev-qa-db-ja.com

C ++でbad_allocを扱う方法は?

時々次のエラーを返すfooというメソッドがあります。

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort

try-catchブロックを使用して、このエラーがプログラムを終了させないようにする方法はありますか(戻りたいのは-1)?

もしそうなら、その構文は何ですか?

bad_alloc C++で?

48
Nosrettap

他の例外と同様にキャッチできます。

try {
  foo();
}
catch (const std::bad_alloc&) {
  return -1;
}

この時点からあなたが便利にできることはあなた次第ですが、技術的には間違いなく実行可能です。

31
Flexo

一般に、このエラーに応答するには、cannotおよびshould not tryを使用します。 bad_allocは、十分なメモリが利用できないためリソースを割り当てることができないことを示します。ほとんどのシナリオでは、プログラムはそれに対処することを望みません。すぐに終了することが唯一の意味のある動作です。

さらに悪いことに、現代のオペレーティングシステムは過剰に割り当てられることがよくあります:mallocnewは、技術的に空きメモリがない(または十分でない)場合でも、常に有効なポインタを返します– so std::bad_allocは決してスローされないか、少なくともメモリ枯渇の信頼できる兆候ではありません。代わりに、割り当てられたメモリにaccessを試みると、エラーが発生しますが、これは捕捉できません。

std::bad_allocをキャッチするときにできる唯一のことは、おそらくエラーをログに記録し、未処理のリソースを解放することでプログラムの安全な終了を保証することです(ただし、これはエラーがスローされた後のスタックの巻き戻しの通常の過程で自動的に行われますプログラムがRAIIを適切に使用している場合)。

場合によっては、プログラムはメモリを解放して再試行するか、RAMではなくセカンダリメモリ(=ディスク)を使用しますが、これらの機会は非常に特定のシナリオにのみ存在します。

79
Konrad Rudolph

C++のnewのC++標準仕様の動作は何ですか?

通常の概念は、new演算子が要求されたサイズの動的メモリを割り当てることができない場合、タイプ_std::bad_alloc_の例外をスローする必要があるということです。
ただし、_bad_alloc_例外がスローされる前でも、さらに何かが発生します。

C++ 03セクション3.7.4.1.3:言う

ストレージの割り当てに失敗した割り当て関数は、現在インストールされているnew_handler(18.4.2.2)を呼び出します(存在する場合)。 [注:プログラム提供の割り当て関数は、set_new_handler関数(18.4.2.3)を使用して、現在インストールされているnew_handlerのアドレスを取得できます。]空の例外仕様(15.4)、throw()で宣言された割り当て関数が失敗した場合ストレージを割り当てると、nullポインターが返されます。ストレージの割り当てに失敗した他の割り当て関数は、クラスstd :: bad_alloc(18.4.2.1)またはstd :: bad_allocから派生したクラスの例外をスローすることによってのみ失敗を示します。

次のコードサンプルを検討してください。

_#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}
_

上記の例では、_operator new_(ほとんどの場合)は100,000,000の整数にスペースを割り当てることができず、関数outOfMemHandler()が呼び出され、プログラムは 発行後に中断しますエラーメッセージ。

ここに見られるように、メモリリクエストを満たすことができない場合のnew演算子のデフォルトの動作は、十分なメモリが見つかるか、新しいハンドラがなくなるまで_new-handler_関数を繰り返し呼び出すことです。上記の例では、std::abort()を呼び出さない限り、outOfMemHandler()繰り返し呼び出される になります。したがって、ハンドラーは、次の割り当てが成功することを確認するか、別のハンドラーを登録するか、ハンドラーを登録しないか、返さない(つまり、プログラムを終了する)必要があります。新しいハンドラーがなく、割り当てが失敗した場合、オペレーターは例外をスローします。

_new_handler_および_set_new_handler_とは何ですか?

_new_handler_は、何も受け取らずに返す関数へのポインターのtypedefであり、_set_new_handler_は、_new_handler_を受け取って返す関数です。

何かのようなもの:

_typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
_

set_new_handlerのパラメーターは、要求されたメモリを割り当てることができない場合に演算子newが呼び出す関数へのポインターです。その戻り値は、以前に登録されたハンドラー関数へのポインター、または以前のハンドラーがなかった場合はnullです。

C++でメモリ不足状態を処理する方法は?

newの動作を考えると、適切に設計されたユーザープログラムは、次のいずれかを行う適切な_new_handler_を提供することにより、メモリ不足状態を処理する必要があります。

利用可能なメモリを増やします:これにより、演算子newのループ内での次のメモリ割り当ての試行が成功する可能性があります。これを実装する1つの方法は、プログラムの起動時にメモリの大きなブロックを割り当て、新しいハンドラが最初に呼び出されたときにプログラムで使用するために解放することです。

別の新しいハンドラをインストールします:現在の新しいハンドラがこれ以上メモリを使用できない場合、および別の新しいハンドラができる場合、その後、現在の新しいハンドラは、その場所に他の新しいハンドラをインストールできます(_set_new_handler_を呼び出して)。次にoperator newがnew-handler関数を呼び出すと、最後にインストールされた関数が取得されます。

(このテーマのバリエーションは、新しいハンドラーが独自の動作を変更することです。そのため、次に呼び出されたときに何か異なることを行います。これを達成する1つの方法は、新しいハンドラーに静的、名前空間固有、新しいハンドラの動作に影響するグローバルデータ。)

new-handlerをアンインストールします:これは、nullポインターを_set_new_handler_に渡すことで実行されます。新しいハンドラがインストールされていない場合、メモリ割り当てが失敗すると、_operator new_は例外((に変換可能)_std::bad_alloc_)をスローします。

_std::bad_alloc_に変換可能な例外をスローします。このような例外は_operator new_によってキャッチされませんが、メモリの要求を発信するサイトに伝播します。

返さない:abortまたはexitを呼び出して。

37
Alok Save

bad_allocは、あなたがメモリ不足であることを意味します。回復しようとするのではなく、あきらめることが最善です。ただし、あなたが求めているソリューションは次のとおりです。

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}
8
Sam Miller

このために、より単純な(さらに高速な)ソリューションを提案する場合があります。メモリを割り当てることができなかった場合、new演算子はnullを返します。

int fv() {
    T* p = new (std::nothrow) T[1000000];
    if (!p) return -1;
    do_something(p);
    delete p;
    return 0;
}

これが役立つことを願っています!

5
TrueY

fooプログラム exit制御された方法で:

#include <stdlib.h>     /* exit, EXIT_FAILURE */

try {
    foo();
} catch (const std::bad_alloc&) {
    exit(EXIT_FAILURE);
}

次に、実際のプログラムを呼び出すShellプログラムを記述します。アドレススペースが区切られているため、シェルプログラムの状態は常に明確に定義されています。

1
Wolf