一个迭代器引用的对象已被其他

时间:2016-11-23 11:00:40

标签: c++ iterator stdlist

我有std::function的列表。

当迭代列表时,将调用该函数,并且在某些情况下,它将在函数调用中被删除。在这种情况下,在for循环中递增迭代器时会发生崩溃,因为迭代器指向不存在的元素。

这是我的代码:

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) {
    if (-1 == it->msg_type || msg_type == it->_msg_type) {
        if (-1 == it->msg_id || response_msg_id == it->_msg_id) {
            it->_msg_handler(msg_type, msg_id, data);
        }
    }
}

用户定义的msg_handler可能会调用remove_callback,其中回调将被其他迭代器擦除。

目前解决方法是在msg_handler调用周围添加++和it。

这是代码

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) {
    if (-1 == it->msg_type || msg_type == it->_msg_type) {
        if (-1 == it->msg_id || response_msg_id == it->_msg_id) {
             std::function<...> handle = it->_msg_handler(msg_type, msg_id, data);
             it++;
             handle(msg_type, msg_id, data);
             it--;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

您可以使用erase并使用其返回值继续循环:

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) {
    if (-1 == msg_type || msg_type == it->_msg_type) {
        if (-1 == msg_id || response_msg_id == it->_msg_id) {
            it->_msg_handler(msg_type, msg_id, data);
        }
    }

    // some case to remove the function

    it = _msg_callbacks.erase(it);
}

来自c std::list::erase上的cppreference.com:

  

返回值

     

跟随最后删除的元素的迭代器。如果迭代器pos引用最后一个元素,则返回end()迭代器。

修改

在你的问题的二读,看起来你的回调实际上删除了该功能。所以你能做的就是让它返回后擦除迭代器:

std::list<MessageCallback>::iterator foo_callback(
    /* decltype */ msg_type,
    /* decltype */ msg_id,
    /* decltype */ data
)
{
    // ...
    // eventually extract iterator it from data; otherwise, you should pass it as argument

    return _msg_callbacks.erase(it);
}

然后把它带回你的循环中:

for (std::list<MessageCallback>::iterator it = _msg_callbacks.begin(); it != _msg_callbacks.end(); ++it) {
    if (-1 == msg_type || msg_type == it->_msg_type) {
        if (-1 == msg_id || response_msg_id == it->_msg_id) {
            it = it->_msg_handler(msg_type, msg_id, data);
        }
    }
}

答案 1 :(得分:1)

list::erase()使所有迭代器,引用或指向擦除元素的指针无效,并将迭代器返回到最后擦除元素后面的元素。如果没有后续元素,则返回结束迭代器。

你的循环没有考虑到这一点。

因此,您需要找到一种方法来返回erase()返回的迭代器,以便它可以在循环中使用。或者(假设你的回调只是擦除传递的迭代器)你的循环可以使用另一个iterator(比如next_it),在调用回调函数之前分配next_it = it + 1,然后设置{{1而不是执行it = next_it