2016-11-23 3 views
-2

私はstd::functionのリストを持っています。1つのイテレータによって参照されるオブジェクトは、他の人によって消去されました

リストを反復すると、関数が呼び出され、場合によっては関数呼び出しで削除されることがあります。この場合、イテレータが既存の要素を指していないため、forループ内のイテレータをインクリメントするときにクラッシュが発生します。ここで

は私のコードです:

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) { 
    if (-1 == it->msg_type || msg_type == it->_msg_type) { 
     if (-1 == it->msg_id || response_msg_id == it->_msg_id) { 
      it->_msg_handler(msg_type, msg_id, data); 
     } 
    } 
} 

ユーザ定義のmsg_handlerは、コールバックが他のイテレータによって消去されるであろうにremove_callback呼び出す可能性があります。

現在、回避策は++とit-- around msg_handler callです。

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) { 
    if (-1 == msg_type || msg_type == it->_msg_type) { 
     if (-1 == msg_id || response_msg_id == it->_msg_id) { 
      it->_msg_handler(msg_type, msg_id, data); 
     } 
    } 

    // some case to remove the function 

    it = _msg_callbacks.erase(it); 
} 

cppreference.comからstd::list::erase上:ここ

はあなたがeraseを使用して、その戻り値を使用してループを継続することができ、コード

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) { 
    if (-1 == it->msg_type || msg_type == it->_msg_type) { 
     if (-1 == it->msg_id || response_msg_id == it->_msg_id) { 
      std::function<...> handle = it->_msg_handler(msg_type, msg_id, data); 
      it++; 
      handle(msg_type, msg_id, data); 
      it--; 
     } 
    } 
} 
+0

あなたの 'msg_type'と' msg_id'はどこですか? –

+0

@appleapple msg_typeとmsg_idが関数のパラメータとして – gonglong

+0

(答えではありません)として渡されますが、ループ内で '-1 == msg_type'をチェックするべきではないと思います –

答えて

1

ある

戻ります値

最後に削除された要素に続くイテレータ。イテレータposが最後の要素を参照している場合は、end()イテレータが返されます。

EDIT:あなたのコールバックが実際に機能を削除しているように、あなたの質問に第二読会で

、それが見えます。だから、あなたはそれが消去後のイテレータを返す作って何ができるのか。そして、

std::list<MessageCallback>::iterator foo_callback(
    /* decltype */ msg_type, 
    /* decltype */ msg_id, 
    /* decltype */ data 
) 
{ 
    // ... 
    // eventually extract iterator it from data; otherwise, you should pass it as argument 

    return _msg_callbacks.erase(it); 
} 

をあなたのループに戻ってそれを取る:

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) { 
    if (-1 == msg_type || msg_type == it->_msg_type) { 
     if (-1 == msg_id || response_msg_id == it->_msg_id) { 
      it = it->_msg_handler(msg_type, msg_id, data); 
     } 
    } 
} 
+0

返信ありがとうございます。しかし、すべてのコールバックのリストは透明でなければならないので、定義されたコールバックでイテレータを返すことは難しいです – gonglong

1

list::erase()が消去要素にすべてのイテレータ、参照、またはポインタを無効に最後に消去された要素に続く要素にイテレータを返します。次の要素がない場合、終了イテレータが返されます。

あなたのループはこれを考慮していません。

したがって、ループ内で使用できるように、erase()によって返されたイテレータを返す方法を見つける必要があります。または、ループで別のiterator(たとえば、next_it)を使用してコールバック関数を呼び出す前にnext_it = it + 1を割り当ててから、++itの代わりにit = next_itを設定することもできます。

+0

あなたの説明に感謝します!あなたの2番目の方法は私の回避策より優れています – gonglong

関連する問題