3

関数 foo(MyClass* mc) が mc のコピーを内部データ構造に保持し、使用されなくなったときにオブジェクトが削除されることを保証する場合を考えてみましょう。

void foo(MyClass* mc) // acquires ownership of mc; may throw
{
  // code that may throw
  bar(mc); // acquires mc; may also throw
}

この関数がスローする可能性のあるコード (たとえば、OutOfMemory 例外) を実行すると、問題が発生します。ポインターがデータ構造に保存される前に例外が発生した場合、呼び出し元はそれに対して責任を負わないため、関数がアンワインドする前にオブジェクトを明らかに解放する必要があります (呼び出し元は、ポインターが実際にオブジェクトに保存されたかどうかさえ知りません)。データ構造かどうか)。

スコープ ガードを使用してこれを処理するために RAII を使用することもできますが、これは非常に扱いにくく、オーバーヘッドが発生します (ポインターを取得するすべての関数で実行する必要があります)。

動的に割り当てられたオブジェクトが取得されるたびにこれを行う必要がありますか、それともより適切な方法がありますか?!

template <class T>
struct VerifyAcq {
  T* ptr;
  bool done;
  VerifyAcq(T* ptr):ptr(ptr) { done = false; }
  ~VerifyAcq() {
    if (!done) delete ptr;
  }
};

void foo(MyClass* mc) // acquires mc; may throw
{
  VerifyAcq<MyClass> va(mc);
  // code that may throw
  bar(mc); // acquires mc; may throw; must implement the same mechanism!
  va.done = true;
}
// Note: there might be no public way of "undoing" what bar has done (no rollbak)
// and even if there was, what if it could also throw?...

ポインターを削除するために呼び出し元が例外をキャッチすることはできません。これは、例外をスローする前に、関数がポインターをデータ構造に正常に追加した可能性があり、オブジェクトを解放するとデータ構造が不安定になる (ダングリング ポインター) ためです。

4

2 に答える 2

4

コードを読み始めたとき、この時点で停止しました。

void foo(MyClass* mc) // acquires ownership of mc; may throw

コメントは、所有権の取得を文書化する最良の方法ではありません。これを行う最善の方法は、型システムを使用することです。

 void foo(std::unique_ptr<MyClass> mc) // no comment required

インターフェイスを修正すると、問題も解決します。

(標準ライブラリに unique_ptr がない場合は、たとえばC++03 での Howard Hinnant のエミュレーションなどの代替手段があります)。

于 2012-12-06T13:28:06.937 に答える
1

RAIIについて言及するとき、あなたは正しい考えを持っています.

あなたが言及したシナリオでは、bar関数が何らかの形でポインターの保存に失敗した場合にのみ削除が発生する必要があることを示しています。そのためには、次のような共有ポインタがboost::shared_ptr非常にうまく機能します。

void foo(boost::shared_ptr<MyClass> mc)
{
    // code that may throw
    bar(mc); // acquires mc; may also throw
}

void bar(boost::shared_ptr<MyClass> mc)
{
    // code that may store the shared_ptr somewhere
}

リソースに対して少なくとも 1 つの共有ポインター (ポインター) がアクティブである限り、そのポインターは削除されません。最後の共有ポインタが破棄またはリセットされるとすぐに、リソースは削除されます。

例:bar関数が共有ポインタを格納する場合、foo関数が終了しても削除は行われません。または、bar関数が共有ポインターの保存に失敗した場合、終了時に削除が行われfooます (リソースに対してアクティブな共有ポインターが他にないと仮定します)。

于 2012-12-06T13:28:12.707 に答える