25

私は常に、std::stringまたはメモリを割り当てる他のクラスをスローすべきではないことを読んでいます。ここのように、またはもっと重要なことに、ポイント 3のここのように。 -オブジェクトを埋め込まないでくださいstd::string

だから今、私は自分のプロジェクトにboost::exceptionを挿入しようとしています

ブーストが独自の推奨事項に準拠しないのはなぜですか?

また、構成ファイルで保護されているように、ハードコーディングできないパラメーターがある場合、使用せずにそれらを例外に入れるにはどうすればよいstd::stringですか?

それとも、ガイドラインはできるだけ使用しないstd::stringというガイドラインを使用していませんか?私は少し混乱しています...std::string

私はいくつかの研究をしました。私が間違っている場合は修正してください。


私がそれを正しく理解していれば、スローの割り当てと、割り当てられたメモリに何が起こっているかがすべてです。したがって、コンストラクターでメモリを割り当てるとメモリが失われ、例外のデストラクタでメモリを解放できないため、メモリリークが発生します。ただし、スローする前にこれを割り当てても問題ないため、例外はクリーンです。

私はこれを試しました:

struct xexception {
  int *ttt[10];
  xexception() {
    ttt[0] = new int[0xfffffffL];
    ttt[1] = new int[0xfffffffL];
    ttt[2] = new int[0xfffffffL];
    ttt[3] = new int[0xfffffffL];
    ttt[4] = new int[0xfffffffL];
    ttt[5] = new int[0xfffffffL];
    ttt[6] = new int[0xfffffffL];
    ttt[7] = new int[0xfffffffL];
    ttt[8] = new int[0xfffffffL];
    ttt[9] = new int[0xfffffffL];
  }

  ~xexception() throw() {
    //never happen
    delete[] ttt[0];
    delete[] ttt[1];
    delete[] ttt[2];
    delete[] ttt[3];
    delete[] ttt[4];
    delete[] ttt[5];
    delete[] ttt[6];
    delete[] ttt[7];
    delete[] ttt[8];
    delete[] ttt[9];
  }
};

int main(int argc, const char *argv[]) {
  try {
    throw(xexception());
  }
  catch (const xexception &e) {
    std::cerr << "\nttt " << e.ttt[0][0] << std::endl;
  }
  catch (std::bad_alloc) {
    std::cerr << "bad alloc" << std::endl;
  }

  return 0;
}

その結果、bad_allocと大量のメモリ リークが発生します。

以前に割り当てを行うと、例外が作成される前に bad_allocもスローされます。


例外の概念に対する私の例外は次のとおりです。

誰も気にしない?プログラムに bad_alloc がある場合、memory_leak など (マイクロコントローラーではなく PC 上のプログラムについて話している) が原因で、他の問題が発生します。bad_alloc が発生したことがわかるかもしれませんが、どこでしょうか? 関数中の割り当て(おそらく1000のうちの1つ)またはstd::string(文字列であることはわかっていますが、...文字列のメモリを操作する可能性はありません...または散逸します)。

try {
  // where is the error???
  int *x = new int[100];  // here?
  ....
  int *y = new int[100];  // or here?
  ....
  int *z = new int[100];
  ....
  int *w = new int[100];
  ....
  int *t = new int[100];
  ....
  int *f = new int[100];

  ....

  std::string str("asdfasdfasdfasdfasdfasdfasdf"); // maybe here
}
catch (the error) {
  ....
}

その後?どこで起きているのか調べてみようかな?したがって、例外ではなくvalgrindを使用します。

void foo() {
  int *i = new int[1];
  foo();
}

try {
  foo();
}
chatch( bad_boy ) {
  go_exception_handler_go(parameters); // oh, shit happens: also an stack_overflow may happend, cause stack is also full
}

または、エラーメッセージを操作してログに記録する必要があります。次のbad_allocを確実にスローします。

誤解しないでください。boost::exception を見たので、(回答を待つまで) 例外クラスを書き直しましたが、すべての砂粒を拾う必要はないと思います。

4

2 に答える 2

18

アドバイスは基本的に、「例外で例外をスローする可能性のある構成を使用しないでください」と言っています。これは、例外をスローしようとしているときに例外が発生した場合、C++ ランタイムはすぐにterminate()プログラムを呼び出して強制終了するためです。

関連する例外の (どちらか) がterminate()とにかく呼び出すだけの場合 (キャッチされない例外のデフォルトのように)、実際にそれについて心配する必要はありません。たとえば、アプリケーションが処理できない場合(メモリ不足から回復できない場合)、それをスローする可能性のあるbad_allocコピー コンストラクター ( など) について心配する必要はありません。std::string

ただし、 をキャッチしてから回復できるようにしたい場合bad_allocは、例外コピー コンストラクターのどれも を発生させないようにする必要があります。他のアプリケーションが使用するライブラリを作成している場合は、アプリケーションが を処理したくないと想定しないでくださいbad_alloc

C++11 では、可能な場合は (コピー コンストラクターの代わりに) 移動コンストラクターを使用することで、これをはるかに簡単にします。ムーブ コンストラクターstd::stringは例外をスローしないため、ムーブ コンストラクターstd:stringを適切に実装し、確実に使用する限り、例外タイプで a を安全に使用できます。throw 式でスローされるオブジェクトの初期構築は、例外スロー プロセスの一部ではないことに注意してください。そのため、コンストラクターは、二重の例外 (および terminate()) を発生させずに例外をスローできます。あなたが持っている場合:

throw some_function();

some_functionbad_allocスローされるオブジェクトを返さずに例外 ( など) をスローする可能性がありますが、それは問題ありません。例外をスローしない (そして有効なオブジェクトを返す) 場合、例外の型のムーブ コンストラクターが (利用可能な場合) 例外スロー プロセスに使用され、そのムーブ コンストラクターは例外をスローしてはなりません。


上記とは完全に独立して、呼び出すときはいつでも、可能なすべてのケースnewで正確に 1 つのスポットが呼び出されるようにする必要があります。そうしないとdelete、メモリ リーク (または二重削除からクラッシュ) が発生します。これは、関数を呼び出しnewてから、例外をスローする可能性のある何か (new再呼び出しなど) を実行する場合は常に注意が必要です。これがコンストラクターで発生した場合、オブジェクトのデストラクタは呼び出されませんが (基本クラスとフィールドのデストラクタは呼び出されます)、例で実行しようとしているようにデストラクタでクリーンアップを行うことはできません。

幸いなことstd::unique_ptrに、これをはるかに簡単にするために存在します。例外クラスを次のように記述した場合:

struct xexception {
  std::unique_ptr<int[]> ttt[10];
  xexception() {
    ttt[0].reset(new int[0xfffffffL]);
    ttt[1].reset(new int[0xfffffffL]);
    ttt[2].reset(new int[0xfffffffL]);
    ttt[3].reset(new int[0xfffffffL]);
    ttt[4].reset(new int[0xfffffffL]);
    ttt[5].reset(new int[0xfffffffL]);
    ttt[6].reset(new int[0xfffffffL]);
    ttt[7].reset(new int[0xfffffffL]);
    ttt[8].reset(new int[0xfffffffL]);
    ttt[9].reset(new int[0xfffffffL]);
  }
};

それは機能し、メモリリークはありません。

于 2013-04-06T17:34:14.173 に答える