1

ゲーム内で順番が必要な時間オブジェクトを参照するポインターのリストがあります。この例ではTimeObject*、リストに 2 つ含まれています。このコードは、アイテムがリストから削除されるまで機能します。削除されると、他のアイテムが指すアドレスは無効になります。TimeObjectこれが発生してもどちらも削除されません。ポインターのみがリストから削除されます。これは何が原因ですか?

TimeUnlink()で呼び出されTimeObject::Tick()ます。これは静的ではありませんが、リストは静的です。

Linux で GCC 4.6.2 を使用しています。プログラムはスレッド化されていません。

void TimeObject::TimeUnlink()
{
    printf("Time unlink\n");

    TimeObject::list_.remove(this);

    timeobject_flags_.linked_ = 0;
}

void GameTime::GameTurn(uint16_t _time)
{
    tick_ += _time;

    for(std::list<TimeObject*>::iterator it = TimeObject::list_.begin(); it != TimeObject::list_.end(); ++it)
    {
        TimeObject *timeobject = *it;

        printf("GameTurn %p\n", timeobject);

        if(timeobject == NULL) { printf("continue\n"); continue; }

        timeobject->time_ += _time;

        if(timeobject->speed_ && timeobject->time_ >= timeobject->speed_)
        {
            while(timeobject->timeobject_flags_.linked_ && timeobject->time_ - timeobject->speed_ > 0)
            {
                timeobject->time_ -= timeobject->speed_;

                if(timeobject->mapobject_)
                {
                    timeobject->mapobject_->Tick();
                }
            }
        }
    }
}

エラー出力:

GameTurn 0xc1e048
GameTurn 0x696828
GameTurn 0xc1e048
GameTurn 0x696828
GameTurn 0xc1e048
GameTurn 0x696828
GameTurn 0xc1e048
Time unlink
GameTurn (nil)
continue
GameTurn 0xc1e030

Program received signal SIGSEGV, Segmentation fault.
0x00000000004059a1 in GameTime::GameTurn(unsigned short) ()
4

4 に答える 4

5

0xc1e048出力シーケンスでは、ポインターは と の間で交互に表示され、それがリストの最初の項目であり、2 番目の項目である0x696828ことを意味します。それに基づいて、がループの途中にある間、 のオブジェクトがリンク解除されているように見えます。リストからオブジェクトを削除すると、オブジェクトを指すイテレータが無効になります。0xc1e0480x6968280xc1e048GameTurn::GameTurntimeobject->mapobject_->Tick()

コードがシングル スレッドであり、 への呼び出しがTick問題の原因であると仮定すると、次のようなものが機能する可能性があります。

void GameTime::GameTurn(uint16_t _time)
{
    tick_ += _time;

    for(std::list<TimeObject*>::iterator it = TimeObject::list_.begin(); it != TimeObject::list_.end(); )
    {
        TimeObject *timeobject = *it;
        ++it;

        printf("GameTurn %p\n", timeobject);

        if(timeobject == NULL) { printf("continue\n"); continue; }

        timeobject->time_ += _time;

        if(timeobject->speed_ && timeobject->time_ >= timeobject->speed_)
        {
            while(timeobject->timeobject_flags_.linked_ && timeobject->time_ - timeobject->speed_ > 0)
            {
                timeobject->time_ -= timeobject->speed_;

                if(timeobject->mapobject_)
                {
                    timeobject->mapobject_->Tick();
                }
            }
        }
    }
}

(唯一の違いは、 のインクリメントがへの代入後にステートメントからループ本体にit移動されることです。無効化される前に進めると、問題が解決するはずです。forittimeobjectit

コードがマルチスレッドの場合、ミューテックスが必要になります。

于 2012-06-15T21:51:10.393 に答える
3

list_ からオブジェクトを削除すると、リスト エントリを削除した後に無効になるため、イテレータを処理する必要があります。

1 つの方法は、アイテムを削除する前に、リスト内の次のエントリを覚えておき、オブジェクトを削除した後にそれを続行することです。

于 2012-06-15T21:43:35.650 に答える
3

呼び出しを行います:

timeobject->mapobject_->Tick();

TimeUnlink() が呼び出されてから、remove が呼び出されますか?

リストから削除すると、削除される要素の反復子は無効になりますが、コードは ++it を実行して引き続き使用しようとする場合があります。

于 2012-06-15T21:43:43.653 に答える
1

s.th を削除しているようです。GameTurn メソッドの反復ループ中にリストから。これにより、イテレータが無効になります。リストから削除するアイテムを覚えておき、ループが終了した後にリストから削除する必要があります。

于 2012-06-15T21:48:08.947 に答える