64

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

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

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

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

bad_allocC ++で他にどのように対処できますか?

4

7 に答える 7

92

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

さらに悪いことに、最近のオペレーティング システムは過剰に割り当てられることがよくあります。このようなシステムではmallocnew十分な空きメモリが残っていなくても有効なポインタを返すことができます。ポインタstd::bad_allocは決してスローされないか、少なくともメモリ枯渇の信頼できる兆候ではありません。代わりに、割り当てられたメモリにアクセスしようとすると、キャッチできないセグメンテーション違反が発生します (セグメンテーション違反信号を処理することはできますが、後でプログラムを再開することはできません)。

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

場合によっては、プログラムが一部のメモリを解放して再試行するか、RAM の代わりにセカンダリ メモリ (= ディスク) を使用しようとすることがありますが、これらの機会は厳しい条件を持つ非常に特殊なシナリオでのみ存在します。

  1. アプリケーションは、メモリをオーバーコミットしないシステムで実行されることを確認する必要があります。つまり、後でではなく割り当て時に失敗を通知します。
  2. アプリケーションは、その間に偶発的な割り当てが発生することなく、すぐにメモリを解放できる必要があります。

アプリケーションがポイント 1 を制御できることは非常にまれです — ユーザー空間アプリケーションが制御できることはありません。これはシステム全体の設定であり、ルート権限の変更が必要です。1

では、ポイント 1 を修正したとしましょう。たとえば、データの一部 (おそらく、必要に応じて再生成または再ロードできる特に大きなビジネス オブジェクト) にLRU キャッシュを使用できます。次に、再試行をサポートする関数に、失敗する可能性のある実際のロジックを配置する必要があります。つまり、中止された場合は、再起動するだけです。

lru_cache<widget> widget_cache;

double perform_operation(int widget_id) {
    std::optional<widget> maybe_widget = widget_cache.find_by_id(widget_id);
    if (not maybe_widget) {
        maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
    }
    return maybe_widget->frobnicate();
}

…

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) {
    try {
        return perform_operation(widget_id);
    } catch (std::bad_alloc const&) {
        if (widget_cache.empty()) throw; // memory error elsewhere.
        widget_cache.remove_oldest();
    }
}

// Handle too many failed attempts here.

ただし、ここでも、std::set_new_handler処理の代わりに使用std::bad_allocすると同じ利点が得られ、はるかに簡単になります。


1コントロール ポイント 1を実行するアプリケーションを作成していて、この回答を読んでいる場合は、私にメールを送ってください。あなたの状況に本当に興味があります。

于 2012-02-26T20:15:59.920 に答える
40

c++でのC++標準指定の動作は何newですか?

通常の概念では、newオペレーターが要求されたサイズの動的メモリーを割り当てることができない場合、 type の例外をスローする必要があります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ここで見られるように、メモリ要求を満たすことができない場合の operator のデフォルトの動作は、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();

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

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

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

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

別の new-handler をインストールする:現在の new-handler がこれ以上メモリを使用可能にできず、別の new-handler が使用できる場合、現在の new-handler はその場所に他の new-handler をインストールできます (を呼び出すことによってset_new_handler)。次に new 演算子が new-handler 関数を呼び出すと、最後にインストールされたものが取得されます。

(このテーマのバリエーションは、new-handler が独自の動作を変更するためのものです。そのため、次に呼び出されたときに別のことを行います。これを実現する 1 つの方法は、new-handler に静的、名前空間固有、またはnew-handler の動作に影響するグローバル データ)。

new-handler をアンインストールします。 これは、null ポインターを に渡すことによって行われますset_new_handler。new-handler がインストールされていない場合、メモリの割り当てに失敗operator newすると、例外 ((convertible to) ) がスローされます。std::bad_alloc

に変換可能な例外をスローしますstd::bad_alloc。このような例外は によってキャッチされませんがoperator new、メモリの要求を発信したサイトに伝搬されます。

返さない:abortまたはを呼び出すことによってexit

于 2012-02-27T03:33:02.473 に答える
39

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

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

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

于 2012-02-26T20:15:35.983 に答える
9

あなたがメモリ不足でbad_allocあることを意味するので、私はこれをお勧めしません。回復しようとするのではなく、あきらめたほうがいいでしょう。ただし、ここにあなたが求めている解決策があります:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}
于 2012-02-26T20:15:35.733 に答える
5

これには、より単純な (そしてさらに高速な) 解決策を提案するかもしれません。newメモリを割り当てることができなかった場合、演算子は null を返します。

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

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

于 2014-06-12T13:06:24.153 に答える
1

foo プログラムを制御された方法で終了させます。

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

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

次に、実際のプログラムを呼び出すシェル プログラムを作成します。アドレス空間が分離されているため、シェル プログラムの状態は常に明確に定義されています。

于 2014-03-12T09:35:20.657 に答える