時々次のエラーを返すfoo
というメソッドがあります。
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Abort
try
-catch
ブロックを使用して、このエラーがプログラムを終了させないようにする方法はありますか(戻りたいのは-1
)?
もしそうなら、その構文は何ですか?
bad_alloc
C++で?
他の例外と同様にキャッチできます。
try {
foo();
}
catch (const std::bad_alloc&) {
return -1;
}
この時点からあなたが便利にできることはあなた次第ですが、技術的には間違いなく実行可能です。
一般に、このエラーに応答するには、cannotおよびshould not tryを使用します。 bad_alloc
は、十分なメモリが利用できないためリソースを割り当てることができないことを示します。ほとんどのシナリオでは、プログラムはそれに対処することを望みません。すぐに終了することが唯一の意味のある動作です。
さらに悪いことに、現代のオペレーティングシステムは過剰に割り当てられることがよくあります:malloc
とnew
は、技術的に空きメモリがない(または十分でない)場合でも、常に有効なポインタを返します– so std::bad_alloc
は決してスローされないか、少なくともメモリ枯渇の信頼できる兆候ではありません。代わりに、割り当てられたメモリにaccessを試みると、エラーが発生しますが、これは捕捉できません。
std::bad_alloc
をキャッチするときにできる唯一のことは、おそらくエラーをログに記録し、未処理のリソースを解放することでプログラムの安全な終了を保証することです(ただし、これはエラーがスローされた後のスタックの巻き戻しの通常の過程で自動的に行われますプログラムがRAIIを適切に使用している場合)。
場合によっては、プログラムはメモリを解放して再試行するか、RAMではなくセカンダリメモリ(=ディスク)を使用しますが、これらの機会は非常に特定のシナリオにのみ存在します。
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です。
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
を呼び出して。
bad_alloc
は、あなたがメモリ不足であることを意味します。回復しようとするのではなく、あきらめることが最善です。ただし、あなたが求めているソリューションは次のとおりです。
try {
foo();
} catch ( const std::bad_alloc& e ) {
return -1;
}
このために、より単純な(さらに高速な)ソリューションを提案する場合があります。メモリを割り当てることができなかった場合、new
演算子はnullを返します。
int fv() {
T* p = new (std::nothrow) T[1000000];
if (!p) return -1;
do_something(p);
delete p;
return 0;
}
これが役立つことを願っています!
fooプログラム exit制御された方法で:
#include <stdlib.h> /* exit, EXIT_FAILURE */
try {
foo();
} catch (const std::bad_alloc&) {
exit(EXIT_FAILURE);
}
次に、実際のプログラムを呼び出すShellプログラムを記述します。アドレススペースが区切られているため、シェルプログラムの状態は常に明確に定義されています。