【问题标题】:erase iterator from list in debug and release mode?在调试和发布模式下从列表中删除迭代器?
【发布时间】:2012-03-14 13:06:18
【问题描述】:

我有一个奇怪的问题。我正在尝试从粒子系统(std::list)中删除迭代器。当粒子离开屏幕时,我删除了一个迭代器。在debug模式下查看粒子大小,发现是release模式下的两倍,不知道为什么。

请找到以下代码

 void ParticleManager::update(std::vector<ci::Vec2f> masses)
    {
        int targetDifference = masses.size() - m_Targets.size();

        if (masses.size() == 1 && targetDifference == 1)
        {
            addTarget();
        }
        else if (targetDifference > 1)
        {
            addTarget();
        } 
        else if (targetDifference < 0)
        {
            deleteTarget();
        }

        Vec2f currVec, offset;
        float fCurrLengthSquared;
        bool bAllTargetsActive = true;

        std::list<Particle>::iterator p = m_Particles.begin();
        while( p != m_Particles.end() ) 
        {
            p->update();

            float fMinSquaredLength = 0.0f;

            // influence of the masses
            for( int i = 0; i < (int) masses.size(); ++i )
            {
                currVec = masses[i] - p->m_Position;
                fCurrLengthSquared = currVec.lengthSquared();

                if (fCurrLengthSquared < ParamMgr.m_fForceMinDist * ParamMgr.m_fForceMinDist)
                {
                    fCurrLengthSquared = ParamMgr.m_fForceMinDist * ParamMgr.m_fForceMinDist;
                }

                if(fCurrLengthSquared < ParamMgr.m_fForceMaxDist * ParamMgr.m_fForceMaxDist)
                {
                    offset = currVec.normalized() / (fCurrLengthSquared / 500.0f);  // 1000.0f      
                    p->m_Direction += offset * ((float) TimerMgr.getDeltaTime() * ParamMgr.m_fGravity * ParamMgr.m_fGravity );
                }

                /*if( i == 0 )
                    fMinSquaredLength = fCurrLengthSquared;

                if( fCurrLengthSquared < fMinSquaredLength )
                    fMinSquaredLength = fCurrLengthSquared;*/
            }

            if( masses.size() > 0 )
            {
                float fSquareColorRadius = ParamMgr.m_fColorRadius * ParamMgr.m_fColorRadius;
                if( fMinSquaredLength > fSquareColorRadius )
                fMinSquaredLength = fSquareColorRadius;

                float fIntensity = 1.0f - (fMinSquaredLength / fSquareColorRadius) * 0.9f;
                //p->m_Color = ci::Color(0.0f, fIntensity, 0.0f);
                //p->m_Color = ci::Color(0.0f, 1.0f, 0.0f);
            }

            p->m_fAge += (float) TimerMgr.getDeltaTime();

            // outside the window
            if( p->m_Position.x < 0.0f - m_fCollisionOffsetX || p->m_Position.x > getWindowWidth() + m_fCollisionOffsetX ||
                p->m_Position.y < 0.0f - m_fCollisionOffsetY || p->m_Position.y > getWindowHeight() + m_fCollisionOffsetY )
            {
                p->m_bIsDead = true;
            }
            else
            { 
                // check targets
                for( std::list<Target>::iterator t = m_Targets.begin(); t != m_Targets.end(); t++ )
                {
                    if( t->checkParticle( p->m_Position ) )
                    {
                        p->m_ColorChange = Color( CM_HSV, t->m_fHue, t->m_fSat * 0.9f, 1.0f );
                        if( p->checkTarget( *t ) )
                        {
                            t->addParticleHit();
                        }
                    }
                    else
                    {
                        if( t != m_Targets.end() )
                        {
                            p->m_Targets.remove( *t );
                            //std::cout << "Try to erase: " << (*t).m_Position << " from target list." << std::endl;
                        }
                    }
                }
            }   

            if( p->m_bIsDead )
            {
                p = m_Particles.erase(p);

            }   
                p++;
}

【问题讨论】:

  • 如何检查发布中的尺寸?
  • 我看到速度取决于实时,这意味着它取决于帧速率。这通常会导致发布和调试之间出现不同的行为。会不会这么简单?
  • @izomorphius 我只是 cout
  • @Agentlien 大小是否也取决于帧率?
  • @AhmedSaleh:这取决于您的实施。此外,如果不是绝对必要,请尽量避免使用std::list。通常,std::vector 速度更快,内存消耗更少,尤其是在一次删除所有对象时。

标签: c++


【解决方案1】:

两点:

  1. 您应该使用p-&gt;m_Targets.erase(t); 而不是p-&gt;m_Targets.remove(*t)。特别是,由于您使用的是 remove(),它接受一个值,因此 std::list 类必须扫描整个列表以找到您指定的值。

  2. 从列表中删除一个元素会使指向该元素的任何迭代器失效。因此,您的 t 迭代器将失效。使用无效的迭代器具有未定义的行为。幸运的是,它(相对)容易解决。试试这样的:

    std::list<Target>::iterator t = m_Targets.begin(); 
    while (t != m_Targets.end())
    {
        if( t->checkParticle( p->m_Position ) )
        {
            p->m_ColorChange = Color( CM_HSV, t->m_fHue, t->m_fSat * 0.9f, 1.0f );
            if( p->checkTarget( *t ) )
            {
                t->addParticleHit();
            }
            ++t;
        }
        else
        {
            t = p->m_Targets.erase(t);
        }
    }
    

请注意,如果我们不删除元素,我们只会执行“++t”(并且在循环的每次迭代中,就像您在 for 循环中所做的那样)。

在您的外循环中有一个类似的错误。替换这个:

        if( p->m_bIsDead )
        {
            p = m_Particles.erase(p);
        }   
        p++;

用这个:

        if( p->m_bIsDead )
            p = m_Particles.erase(p);
        else
            ++p;

【讨论】:

  • 感谢您的回复。使用您的建议,它具有相同的性能问题。那些行呢: if( p->m_bIsDead ) { p = m_Particles.erase(p); // std::cout
  • @AhmedSaleh:更新响应以描述外部循环中的错误,以及如果您刚刚删除一个元素,您不应该增加您的迭代器,因为这会导致您跳过一个元素。
  • 这一行编译失败 t = p->m_Targets.remove(t);我什至用过 t = p->m_Targets.erase(t);它崩溃了
猜你喜欢
  • 1970-01-01
  • 2016-07-19
  • 2014-02-18
  • 2012-04-01
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
  • 2011-06-14
  • 2011-10-30
相关资源
最近更新 更多