【问题标题】:std::list of pointers becomes invalid on entry removalstd::list of pointers 在条目删除时变得无效
【发布时间】:2012-06-15 21:26:38
【问题描述】:

我有一个指针列表,这些指针引用了我的游戏中需要转弯的时间对象。此示例在列表中有两个 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) ()

【问题讨论】:

  • 是否有多个线程访问和修改list_
  • TimeUnlink 函数在哪里被调用?这个函数或列表是静态成员吗​​?
  • TimeUnlink()TimeObject::Tick() 期间被调用
  • 可以显示Tick函数吗??
  • 顺便说一句,在您的容器中使用智能指针(或根本不使用指针)。因为您使用指针,您需要手动释放每个项目(即调用delete),从而使容器无法为您管理内存。

标签: c++ pointers segmentation-fault stdlist


【解决方案1】:

在您的输出序列中,指针在0xc1e0480x696828 之间交替,这意味着0xc1e048 是列表中的第一项,0x696828 是第二项。基于此,看起来0xc1e048 处的对象正在取消链接,而GameTurn::GameTurn 处于循环中间,很可能是在对timeobject-&gt;mapobject_-&gt;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();
                }
            }
        }
    }
}

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

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

【讨论】:

  • 我最终选择了一些非常相似的东西。尽管它是单线程的,但我重新设计了它以便它可以是多线程的。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    是否通话:

    timeobject->mapobject_->Tick();
    

    导致 TimeUnlink() 被调用,然后 remove 被调用?

    当您从列表中删除时,被删除元素的迭代器将变为无效,但您的代码可能会继续尝试通过执行 ++it 来使用它。

    【讨论】:

    【解决方案4】:

    看起来你正在删除 s.th。在 GameTurn 的迭代循环期间从列表中 方法。这使得迭代器无效。您必须记住要从列表中删除的项目,并在循环结束后从列表中删除这些项目。

    【讨论】:

      猜你喜欢
      • 2012-07-05
      • 2012-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-13
      • 1970-01-01
      • 2020-08-06
      • 2016-07-15
      相关资源
      最近更新 更多