【问题标题】:Vector iteration and deletion向量迭代和删除
【发布时间】:2015-04-01 23:51:39
【问题描述】:

我正在编写一个多线程 c++ WIN 套接字服务器,但遇到了一些奇怪的问题。

我正在使用向量来存储活动连接。我用 win mutex 锁定向量,然后尝试遍历它以找到所有关闭的连接并删除它们,然后释放 mutex。

代码:

if (!m_activeConnections.empty()){
    for(std::vector<Connection*>::iterator it = m_activeConnections.begin(); it != m_activeConnections.end(); ++it) {
        if ((*it)->isClosed()){
            delete *it;
            it = m_activeConnections.erase(it);
            break;
        }
    }
    cout << "\n \t Active Connections: " << m_activeConnections.size() << endl;
}

它的工作原理是这样的,但是当我删除 break 行时,它总是会再循环一次,迭代器 it 指向 0Xaaaaaa 并引发异常。如果此删除是在创建新连接的同一线程中完成的,即使没有中断,它也可以正常工作。这是为什么呢?

【问题讨论】:

    标签: c++ vector iterator iteration


    【解决方案1】:

    无论何时修改范围,都必须确保更新用于遍历该范围的迭代器的方式与从范围中擦除时迭代器失效的方式兼容。

    将这种方法应用于向量的一个简单示例是以下循环。请注意,从擦除点开始擦除会使所有迭代器失效,因此您需要在擦除时获得一个新的迭代器,并且您还需要每次重新计算 end()(即不要将结束计算提升到循环之外) :

    for (auto it = m_activeConnections.begin(); it != m_activeConnections.end(); )
    {
        if ((*it)->isClosed()) { it = m_activeConnections.erase(it); }
        else                   { ++it;                               }
    }
    

    从向量中擦除的更好方法是将要擦除的元素移动到向量的后面,然后一次擦除整个范围,避免一直移动尾部范围。通常我们使用remove_if 执行此操作,但您需要添加一些技巧来删除您的案例中的指针:

    m_activeConnections.erase(
        std::remove_if(m_activeConnections.begin(),
                       m_activeConnections.end(),
                       [](Connection * p) {
                          if (p->isClosed()) { delete p; return true; }
                          return false;}),
        m_activeConnections.end());
    

    如果将容器更改为 std::vector&lt;std::unique_ptr&lt;Connection&gt;&gt;,则可以避免这种诡计:每个职责有一个类(向量包含,唯一指针删除),算法变得可组合。

    如果你不能通过选择合适的抽象来简化你的代码,你也可以尝试一个更复杂的算法:先根据需要对范围进行分区删除,然后删除,再擦除范围:

    auto it = std::stable_partition(m_activeConnections.begin(),
                                    m_activeConnections.end(),
                                    [](Connection * p) { return p->isClosed(); });
    
    for (auto kt = it; kt != m_activeConnections.end(); ++kt)
    {
        delete *kt;
    }
    
    m_activeConnections.erase(it, m_activeConnections.end());
    

    【讨论】:

    • 哦,谢谢,我是 C++ 的初学者。我习惯了java并且没有注意这一点。谢谢。
    • 实际上,如果目标是同时delete 项目然后删除项目,remove_if 将在这里不起作用。如果要使用算法方法,则先std::stable_parttion,然后delete,并擦除分区“坏”侧的项目。
    猜你喜欢
    • 1970-01-01
    • 2021-11-08
    • 2017-10-24
    • 2021-01-02
    • 1970-01-01
    相关资源
    最近更新 更多