graph (3)
並列処理以外にも時間変化するリストには注意点が考えられるでしょう.そのひとつが,現在走査中のリスト自身に対する破壊的操作です.以下のようなバグは C++ でよく見かけます.
std::list<Character> list; // 誤り for( std::list<Character>::iterator i = list.begin(); i != list.end(); ++i ) { if( i->CanErase() ) { list.erase( i ); } }
正しくはこうする必要があります.
std::list<Character> list; std::list<Character>::iterator i = list.begin(); while( i != list.end() ) { if( i->CanErase() ) { i = list.erase( i ); } else { ++i; } }
上の例は STL の iterator の理解不足で片付くかもしれませんが,以下のように DoSomething の内部でリストの破壊的変更が引き起こされてしまう場合はなかなか簡単には気付けません.
class Manager { std::list<Character> list; void DoSomething( Character& c ) { // 分裂可能な場合は分裂する if( c.Splitting() ) { list.push_back( CreateCharactor(c) ); } } void MainAll() { for( std::list<Character>::iterator i = list.begin(); i != list.end(); ++i ) { DoSomething(*i); } } };
実際ゲームプログラミングでこのパターンのバグを何度か発見したことがあります.フレームごとの各キャラクタに対する処理のフェイズで,あるキャラクタの条件によって別のキャラクタを削除したり追加したりしてしまっていて,追加したキャラクタに対する処理が予想より 1 フレーム早かったり遅かったりというバグでした.