映射/集合迭代器不可递增。

16
Driver::~Driver()
{
    AutoCritSec acsDriverList(m_csDriverList,true);
    DRIVERLIST::iterator it = m_DriverList.begin();
    for(;it!=m_DriverList.end();it++) 
    {
        if (it->second == this) 
        {
            m_DriverList.erase(it);
            it = m_DriverList.begin();
        }
    }
}

我在Visual Studio 2003中编译我的程序时,程序运行良好。但是当我在2010中进行相同的操作时,在关闭应用程序时会出现一些错误。

Expression:map/set iterator not incrementable

当我按下忽略时,我得到

Expression:"standard c++ library out of range" && 0

有没有人知道这里发生了什么:如果有人有任何建议,我将非常感激。万分感谢和热情祝福。

2个回答

16
如果列表中只有这个元素,您将超出列表的末尾。 从列表中删除此元素后,重置“it = m_DriverList.begin();”是可以的。然后,循环表达式被求值(来自“for”语句的“i++”),导致“it”超过范围的末尾。 将迭代器推进到容器的末尾会导致程序表现出未定义的行为。Visual C++的最新版本在调试构建程序中有助于检测许多常见的迭代器错误并引发断言以帮助您解决它们。 您可以通过删除循环表达式并将其移动到“else”语句中来解决问题。
while (it != m_DriverList.end())
{
    if (it->second == this)
    {
        m_DriverList.erase(it);
        it = m_DriverList.begin();
    }
    else
    {
        ++it;
    }
}

然而,每次删除一个元素后重新开始迭代是相当浪费的。 相反地,考虑使用调用 erase 返回的迭代器:

it = m_DriverList.erase(it);

好的删除建议。在这种情况下,m_DriverList显然是一对容器或某种映射,因为测试是在it->second上进行的。它将需要使用带有lambda或比较函数的std::remove_if而不是std::remove。 - Mark Taylor
如果容器是一个映射(问题标题,访问成员“second”),那么我认为无法应用erase-remove惯用语。这个惯用语类似于您拥有的while循环,但是不是重新启动迭代,而是复制并推进迭代器,然后删除当前位置。 - David Rodríguez - dribeas
@DavidRodríguez-dribeas @MarkTaylor:发现得好;我被变量名中的“List”分散了注意力。在C++11中,erase返回下一个元素的迭代器(如果没有下一个元素,则返回结束时的迭代器),Visual C++ 2010支持此功能。 - James McNellis
@JamesMcNellis,是的,在C++03中缺少了这个功能,其中erase函数仅为序列返回下一个迭代器,但不适用于关联容器。对于关联容器,这项工作必须在C++03中手动完成。 - David Rodríguez - dribeas
你不需要使用begin重新启动循环。相反,在erase()调用中使用POST-increment it++。这样做的原因是它的值被写下来供erase()使用,但在实际调用erase()并删除指向的内容之前,它已经被增加了。 - Swiss Frank

14

关于关联容器的正确删除惯用法如下:

for (auto it = container.begin(); it != container.end() /* not hoisted */; /* no inc. */ )
{
    if (delete_condition)
    {
        container.erase(it++);
    }
    else
    {
        ++it;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接