🔖
[C++] new実行時にbad_allocを起こしたくないならstd::nothrowを指定するとよい
C++でnewを使うとプログラムがヒープ上に動的にメモリが確保しにいくが、確保できる領域が無い場合std::bad_allocの例外が発生する。
そのため、newを使う各々の箇所でtry-catch処理を入れなければいけない。
int* ary;
try
{
ary = new int[1000];
}
catch (const std::bad_alloc&)
{
std::cout << "Allocation failed" << std::endl;
}
しかし全てのnewをしている箇所でtry-catchを入れるのは面倒なので調べていたところ、std::nothrowというキーワードをnew実行時に指定すれば例外が起きないということを知った。
std::nothrowを使い実装したコードは以下。
int* ary = new (std::nothrow) int[1000];
if (ary == nullptr)
{
std::cout << "Allocation failed" << std::endl;
}
メモリ確保に失敗した場合、bad_allocの例外は発生せずnullptrがnewから返る。
そのためtry-catch処理は不要。
null判定してエラー処理してあげれば良いのでこちらの方がシンプルで良い。
Discussion
例外はそれをキャッチする記述があるところまで関数の呼び出し元へ遡って処理されるので対処方法が共通である場合は毎回その場に
try
/catch
を書く必要はありません。 特にメモリの確保を失敗するような状況では出来ることがほとんどない (メッセージを出して終了するくらいしか出来ない) のでmain
にひとつ書けば充分だということも多いでしょう。注意が必要な点として、呼び出し元へ遡る過程で自動変数は解体される[1]のですが、デストラクタ以外の形でリソースの後始末をしているとその処理を通過しないということです。 上述の例で言えば関数
bar
内から例外が送出されたときはfoo
内のx
(が指している配列) は解放されないままになります。例外をキャッチして必要な処置をした後で例外を再送出するという手順をとってもよいのですが「後始末はデストラクタでやる」ということを原則として守れていれば煩雑な記述を避けられるでしょう。 メモリの管理に生ポインタを使うのはなるべく避けてスマートポインタを活用するのが好ましいのはこういったときにプログラマが注意深く考えなくても後始末がされるからです。
現在の仕様[2]では標準ライブラリが充実しているのでプログラマが直接的に
new
を使う機会というのはそれほど多くありません。 学習の段階ではメカニズムを理解するために基礎的な機能に積極的に触れるのは望ましいのですが、その段階の判断で「例外を避けたほうが楽」とは思わないほうがよいと思います。 色々な機能との組み合わせで考えると例外が便利なこともあります。デストラクタが呼び出されてその領域の解放もされる、いわゆるスタックの巻き戻し (stack unwinding) が起こる ↩︎
C++14 以降くらいから ↩︎