矢量迭代和删除 [英] Vector iteration and deletion

查看:168
本文介绍了矢量迭代和删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am programming a multithreaded c++ WIN socket server and I came across some weird problem.

我使用向量来存储活动连接。我用win mutex锁定向量然后我尝试迭代它以找到所有已关闭的连接并删除它们然后释放互斥锁。

I am using a vector to store active Connections. I lock the vector with win mutex then I try to iterate over it to find all closed connections and delete them then release 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;
}

它是这样的,但当我删除打破行它总是再循环一次迭代器指向0Xaaaaa并抛出异常。如果删除是在创建新Connections的同一个线程中完成的,即使没有中断也可以正常工作。这是为什么?

It works like this, but when I remove the break line it always goes one more loop with iterator it pointing at 0Xaaaaaa and throws exception. If this deletion is done in the same thread where the new Connections are created it works just fine even without the break. Why is this?

推荐答案

每当修改范围时,都必须确保更新用于遍历的迭代器当你从范围中删除时,范围与迭代器失效的方式兼容。

Whenever you modify a range, you have to make sure that you update the iterator that you use to traverse the range in a way that's compatible with the way iterators are invalidated when you erase from the range.

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

A simple example for applying this approach to a vector is the following loop. Note that erasing invalidates all iterators from the point of erasure onwards, so you need to obtain a fresh iterator when you erase, and you also need to recompute end() each time (i.e. don't hoist the end computation out of the loop):

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

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

A better way to erase from a vector is to move the to-be-erased elements to the back of the vector and then erasing the whole range all at once and avoid moving the tail range around all the time. Generally we do this with remove_if, though you need to add a bit of trickery to also delete the pointees in your case:

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<,你可以避免这种诡计; std :: unique_ptr< Connection>> :每个职责都有一个类(向量包含,唯一指针删除),算法可以组合。

You could avoid that trickery if you changed your container to std::vector<std::unique_ptr<Connection>>: Have one class per responsibility (the vector contains, the unique pointer deletes), and algorithms become composable.

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

If you can't make your code simple by choosing appropriate abstractions, you can also try a more complicated algorithm: Partition the range first according to the need to delete, then delete, then erase the range:

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());

这篇关于矢量迭代和删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆