25

簡単な質問があります。例外が発生したときに、tryブロックに割り当てられた1つのメモリをどのように解放しますか?次のコードを検討してください。

try
 {
  char *heap = new char [50];
        //let exception occur here
  delete[] heap;
 }
 catch (...)
 {
  cout << "Error, leaving function now";
  //delete[] heap; doesn't work of course, heap is unknown to compiler
  return 1;
 }

ヒープが割り当てられ、呼び出し前に例外が発生した後、どうすればメモリを解放できますdelete[] heapか?これらのtry..catchブロックのヒープにメモリを割り当てないというルールはありますか?

4

9 に答える 9

34

RAIIイディオムリソース獲得は初期化です)を研究してください!たとえば、RAIIに関するウィキペディアの記事を参照してください。

RAIIは単なる一般的な考え方です。これは、たとえばC++標準ライブラリstd::unique_ptrまたはstd::shared_ptrテンプレートクラスで使用されます。


RAIIイディオムの非常に簡単な説明:

try..finally基本的に、これは他のいくつかの言語で見られるブロックのC++バージョンです。RAIIイディオムは間違いなくより柔軟です。

それはこのように動作します:

  • リソース(メモリなど)の周りにラッパークラスを記述します。デストラクタは、リソースを解放する責任があります。

  • ローカル(自動)変数として、スコープ内のラッパークラスのインスタンスを作成します。プログラムの実行がそのスコープを離れると、オブジェクトのデストラクタが呼び出され、それによってリソース(メモリなど)が解放されます。

重要な点は、スコープがどのように終了するかは問題ではないということです。例外がスローされた場合でも、スコープは終了し、ラッパーオブジェクトのデストラクタが呼び出されます。


非常に大雑把な例:

// BEWARE: this is NOT a good implementation at all, but is supposed to
// give you a general idea of how RAII is supposed to work:
template <typename T>
class wrapper_around
{
  public:
    wrapper_around(T value)
        : _value(value)
    { }
    T operator *()
    {
        return _value;
    }
    virtual ~wrapper_around()
    {
        delete _value;  // <-- NOTE: this is incorrect in this particular case;
                        // if T is an array type, delete[] ought to be used
    }
  private:
    T _value;
};
// ...

{
    wrapper_around<char*> heap( new char[50] );
    // ... do something ...

    // no matter how the { } scope in which heap is defined is exited,
    // if heap has a destructor, it will get called when the scope is left.
    // Therefore, delegate the responsibility of managing your allocated
    // memory to the `wrapper_around` template class.
    // there are already existing implementations that are much better
    // than the above, e.g. `std::unique_ptr` and `std::shared_ptr`!
}
于 2010-06-15T19:32:58.423 に答える
9

OKミスターJavaプログラマー:

try
{
    // Exception safe dynamic allocation of a block of memory.
    std::vector<char>  heap(50);

    // DO STUFF

    // Note in C++ we use stack based objects and their constructor/destructor
    // TO give a deterministic cleanup, even in the presence of exceptions.
    //
    // Look up RAII (bad name for a fantastic concept).
}
catch (...)
{
    cout << "Error, leaving function now";
    return 1;  // Though why you want to return when you have not fixed the exception is
               // slightly strange. Did you want to rethrow?
}
于 2010-06-15T19:46:18.613 に答える
9

一般的な答えは、RAIIを使用することです。

ただし、変数をtry{}スコープの外に移動することで解決できます。

char * heap = NULL;
try {
  heap = new char [50];
  ... stuff ...
} catch (...) {
  if (heap) {
    delete[] heap;
    heap = NULL;
  }
  ... however you want to handle the exception: rethrow, return, etc ...
}

私はこれを良い習慣として推奨していないことに注意してください-しかし、あなたが本当にリスクを知っていて、それでもそれらを受け入れる気がある場合にのみ、より多くのダウン&ダーティを使用することをお勧めします。個人的にはRAIIを使います。

平和

于 2010-06-15T20:38:37.933 に答える
6

newの前に移動しtryて、ポインタがまだスコープ内にあるようにするか、またはのようなスマートポインタを使用しますshared_ptrunique_ptrピンチではauto_ptr、ですが、問題があります)。これにより、終了時にクリーンアップされます。例外は、スマートポインタが重要である大きな理由です。

于 2010-06-15T19:34:36.250 に答える
3

「正しい」答えは、上記のようにRAIIとshared_ptrですが、完全を期すために、あなたの例では、次のように置き換えることができます。

char *heap = new char [50];

char *stack = static_cast<char*>( alloca(50) );

allocaは、ヒープではなくスタックにメモリを割り当てることを除いて、ほぼ同じですmalloc。したがって、どのように機能が終了しても(スローまたは現在)、メモリは再利用され、削除や解放は必要ありません。

于 2010-06-15T19:55:27.430 に答える
1

私はRAIIについて述べたすべての人に同意する必要がありますが、auto_ptrの代わりにBoostのshared_arrayを使用します。自動ポインタ呼び出しdeleteであり、配列でリークを引き起こす'delete[]'ではありません。

于 2010-06-15T19:56:49.927 に答える
0

最も簡単な方法は、tryブロックの前に変数を宣言してから、ブロック内で初期化を行うことです。

于 2010-06-15T19:35:51.683 に答える
0

はい-単純さを考慮している場合-tryブロックの外側にあるポインターが解決策です。

よろしく

于 2010-06-23T14:24:37.533 に答える
-4

RAIIとスマートポインターに関する回答に同意しました。

ただし、主張する場合は、これを行うことができます。

try { dangerous operations } 
catch { cleanup; throw; }
于 2010-06-15T19:37:31.777 に答える