2

だからここに私が扱っているコードがあります:

class A
{
public:
    A(){}
    virtual ~A(){}
    void Log(){printf("Log A\n");}
};

int main(int argc, char**argv) 
{
    A* a = new A();

    a->Log(); // "Log A"

    map<int,A*> m;
    m[1] = a;
    m[2] = a;
    m[3] = a;

    m[1]->Log(); // "Log A"

    delete a;
    a = NULL;

    m[1]->Log(); // "Log A"

    return 0;
}

出力:

ログA
ログA
ログA

私の質問:

  1. m[1]->Log()呼び出しが後に例外をスローしないのは偶然delete aですか?
  2. の削除されたインスタンスを指すマップ内のすべてのエントリを消去するための最良のアプローチは何Aですか?つまり、すべてが必要m.find(1)で、削除した後m.find(2)m.find(3)戻るということです。何かアドバイスをいただければ幸いです。m.end()a
4

4 に答える 4

5
  1. はいといいえ。技術的には未定義の動作ですが、通常(これに依存しないでください)、メンバーにアクセスしない非仮想メソッドの呼び出しは、ほとんどのコンパイラで逆参照せずにこの呼び出しを実装するためthis(これは無効な部分です)、ほとんどのコンパイラで機能するようです。したがって、標準の意見では、それは偶然です。ほとんどのコンパイラーでは、それが意図されています(または少なくとも関数呼び出しの処理方法の副作用)。

  2. 代わりにスマートポインタを使用してください。要素を削除するには、マップを反復処理して、各値を自分の値と比較します。1つに到達したら、を使用しますeraseイテレータは消去後に無効になります。

于 2012-05-29T12:30:08.627 に答える
4
  1. 削除されたオブジェクトを逆参照するときに発生することはすべて未定義の動作であるため、例外が発生した場合でも「偶然」と見なされる可能性があります。

  2. 最善のアプローチは、ポイントされたオブジェクトの削除と、ハンドルを持っているものの存続期間を組み合わせることです。したがって、あなたの場合、ポインタがマップから削除された場合、オブジェクトが削除された方がよいと判断できます。std::unique_ptr<A>これを実現するには、生のポインタの1つではなく、inttoのマップを使用できます。

編集:要件をより詳細に検討した後:

ここで、マップされたタイプが削除されたオブジェクトを指している要素を削除したいので、ポインターが指しているメモリが削除されたかどうかを判断する方法がないため、これらのエントリをマップから削除する簡単な方法はありません。すべてを1回の関数呼び出しで。そして、std::map他は好きstd::remove_ifではないので、ループを使用することができます:

template <typename T1, typename T2>
void removeEntriesAndDelete(std::map<T1, T2*>& m, T2*& item) {
  for (auto i = m.begin(); i != m.end(); ) {
    if ( item == i->second) {
      m.erase(i++);
    } else {
      ++i;
    }
  }
  delete item;
  item=0;
} 

int main() {

  A* a = new A;
  std::map<int,A*> m;
  m[1] = a;
  m[2] = a;
  m[3] = a;
  std::cout << std::boolalpha;
  std::cout << a << ", " << bool(m[1]) << ", " << bool(m[2]) << ", " << bool(m[3]) <<"\n";

  removeEntriesandDelete(m, a);
 std::cout << a << ", " << bool(m[1]) << ", " << bool(m[2]) << ", " << bool(m[3]) <<"\n";
}
于 2012-05-29T12:33:43.990 に答える
2

一般に、どこかに登録されたオブジェクトは、登録された場所をオブジェクトに通知する必要があります。サンプルコードのような単純なケースでは、オブジェクトを指す各要素を消去してマップを調べるのは比較的簡単ですが、実際のユースケースはそれほど簡単ではないと思います。通常の解決策(実際に実際に機能する唯一の解決策)には、オブザーバーパターンが含まれます。オブジェクトがオブジェクトへのポインタを直接またはマップやリストなどに保存すると、オブジェクトも登録され、オブジェクトが破棄されたときに通知を要求します。オブジェクトはこれらのオブザーバーのリストを保持し、デストラクタでそれらに通知します。あなたのような単純なケースに適用すると、多くの不要なコードのように見えますが、このパターンが発生する実際のアプリケーションのコンテキストでは、それほど多くはありません。

于 2012-05-29T13:46:58.313 に答える
1

おっしゃるように運が良ければうまくいきます。

前述のようにスマートポインタのようなものを使用するか、マップと値の処理をクラスにカプセル化します。この場合、マップからオブジェクトを削除してオブジェクトを削除するメソッドを使用できます

于 2012-05-29T12:32:26.377 に答える