1

我有一个指针列表,这些指针引用了我的游戏中需要转弯的时间对象。此示例TimeObject*在列表中有两个。此代码一直有效,直到从列表中删除一个项目:当发生这种情况时,另一个指向的地址将变为无效地址。发生这种情况时,两者都不会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在您的输出序列中,指针在和之间交替0x696828,这意味着它0xc1e048是列表中的第一项并且0x696828是第二项。基于此,看起来 at 的对象在循环中间0xc1e048时被取消链接GameTurn::GameTurn,很可能是在对 的调用timeobject->mapobject_->Tick()或@hmjd 提到的另一个线程中。从列表中删除一个对象会使指向该对象的迭代器无效。

假设代码是单线程的并且调用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();
                }
            }
        }
    }
}

(唯一的区别是在分配给之后itfor语句移动到循环体的增量。在它失效之前推进应该可以解决问题。ittimeobjectit

如果您的代码是多线程的,您将需要一个互斥锁。

于 2012-06-15T21:51:10.393 回答
3

当您从 list_ 中删除对象时,您需要注意迭代器,因为在您删除列表条目后它变得无效

一种方法是在删除项目之前,记住列表中的下一个条目,并在删除对象后继续。

于 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 回答